1 //---------------------------------------------------------------------
2 // <copyright file="ObjectStateEntry.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 using System.Collections.Generic;
10 using System.Data.Common;
11 using System.Data.Metadata.Edm;
12 using System.Data.Objects.DataClasses;
13 using System.Diagnostics;
14 using System.Collections;
16 namespace System.Data.Objects
20 // Added - _entity & _currentValues only for shadowState
22 // Unchanged - _entity & _currentValues only for shadowState
23 // Unchanged -> Deleted - _entity & _currentValues only for shadowState
25 // Modified - _currentValues & _modifiedFields + _originalValues only on change
26 // Modified -> Deleted - _currentValues & _modifiedFields + _originalValues only on change
29 /// Represets either a entity, entity stub or relationship
31 public abstract class ObjectStateEntry : IEntityStateEntry, IEntityChangeTracker
33 #region common entry fields
34 internal ObjectStateManager _cache;
35 internal EntitySetBase _entitySet;
36 internal EntityState _state;
40 // ObjectStateEntry will not be detached and creation will be handled from ObjectStateManager
41 internal ObjectStateEntry(ObjectStateManager cache, EntitySet entitySet, EntityState state)
43 Debug.Assert(cache != null, "cache cannot be null.");
46 _entitySet = entitySet;
49 #endregion // Constructor
51 #region Public members
53 /// ObjectStateManager property of ObjectStateEntry.
56 /// <returns> ObjectStateManager </returns>
57 public ObjectStateManager ObjectStateManager
66 /// <summary> Extent property of ObjectStateEntry. </summary>
68 /// <returns> Extent </returns>
69 public EntitySetBase EntitySet
79 /// State property of ObjectStateEntry.
82 /// <returns> DataRowState </returns>
83 public EntityState State
96 /// Entity property of ObjectStateEntry.
99 /// <returns> The entity encapsulated by this entry. </returns>
100 abstract public object Entity { get; }
103 /// The EntityKey associated with the ObjectStateEntry
105 abstract public EntityKey EntityKey { get; internal set; }
108 /// Determines if this ObjectStateEntry represents a relationship
110 abstract public bool IsRelationship { get; }
113 /// Gets bit array indicating which properties are modified.
115 abstract internal BitArray ModifiedProperties { get; }
117 BitArray IEntityStateEntry.ModifiedProperties { get { return this.ModifiedProperties; } }
120 /// Original values of entity
123 /// <returns> DbDataRecord </returns>
124 [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this
125 abstract public DbDataRecord OriginalValues { get; }
127 abstract public OriginalValueRecord GetUpdatableOriginalValues();
130 /// Current values of entity/ DataRow
133 /// <returns> DbUpdatableDataRecord </returns>
134 [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this
135 abstract public CurrentValueRecord CurrentValues { get; }
138 /// API to accept the current values as original values and mark the entity as Unchanged.
141 /// <returns></returns>
142 abstract public void AcceptChanges();
145 /// API to mark the entity deleted. if entity is in added state, it will be detached
148 /// <returns> </returns>
149 abstract public void Delete();
152 /// API to return properties that are marked modified
155 /// <returns> IEnumerable of modified properties names, names are in term of c-space </returns>
156 abstract public IEnumerable<string> GetModifiedProperties();
159 /// set the state to Modified.
162 /// <returns></returns>
163 /// <exception cref="InvalidOperationException">If State is not Modified or Unchanged</exception>
165 abstract public void SetModified();
168 /// Marks specified property as modified.
170 /// <param name="propertyName">This API recognizes the names in terms of OSpace</param>
171 /// <exception cref="InvalidOperationException">If State is not Modified or Unchanged</exception>
173 abstract public void SetModifiedProperty(string propertyName);
176 /// Rejects any changes made to the property with the given name since the property was last loaded,
177 /// attached, saved, or changes were accepted. The orginal value of the property is stored and the
178 /// property will no longer be marked as modified.
181 /// If the result is that no properties of the entity are marked as modified, then the entity will
182 /// be marked as Unchanged.
183 /// Changes to properties can only rejected for entities that are in the Modified or Unchanged state.
184 /// Calling this method for entities in other states (Added, Deleted, or Detached) will result in
185 /// an exception being thrown.
186 /// Rejecting changes to properties of an Unchanged entity or unchanged properties of a Modifed
189 /// <param name="propertyName">The name of the property to change.</param>
190 abstract public void RejectPropertyChanges(string propertyName);
193 /// Uses DetectChanges to determine whether or not the current value of the property with the given
194 /// name is different from its original value. Note that this may be different from the property being
195 /// marked as modified since a property which has not changed can still be marked as modified.
198 /// For complex properties, a new instance of the complex object which has all the same property
199 /// values as the original instance is not considered to be different by this method.
201 /// <param name="propertyName">The name of the property.</param>
202 /// <returns>True if the property has changed; false otherwise.</returns>
203 abstract public bool IsPropertyChanged(string propertyName);
206 /// Returns the RelationshipManager for the entity represented by this ObjectStateEntry.
207 /// Note that a RelationshipManager objects can only be returned if this entry represents a
208 /// full entity. Key-only entries (stubs) and entries representing relationships do not
209 /// have associated RelationshipManagers.
211 /// <exception cref="InvalidOperationException">The entry is a stub or represents a relationship</exception>
212 abstract public RelationshipManager RelationshipManager
218 /// Changes state of the entry to the specified <paramref name="state"/>
220 /// <param name="state">The requested state</param>
221 abstract public void ChangeState(EntityState state);
224 /// Apply modified properties to the original object.
226 /// <param name="current">object with modified properties</param>
227 abstract public void ApplyCurrentValues(object currentEntity);
230 /// Apply original values to the entity.
232 /// <param name="original">The object with original values</param>
233 abstract public void ApplyOriginalValues(object originalEntity);
235 #endregion // Public members
237 #region IEntityStateEntry
238 IEntityStateManager IEntityStateEntry.StateManager
242 return (IEntityStateManager)this.ObjectStateManager;
246 // must explicitly implement this because interface is internal & so is the property on the
247 // class itself -- apparently the compiler won't let anything marked as internal be part of
248 // an interface (even if the interface is also internal)
249 bool IEntityStateEntry.IsKeyEntry
253 return this.IsKeyEntry;
256 #endregion // IEntityStateEntry
258 #region Public IEntityChangeTracker
261 /// Used to report that a scalar entity property is about to change
262 /// The current value of the specified property is cached when this method is called.
264 /// <param name="entityMemberName">The name of the entity property that is changing</param>
265 void IEntityChangeTracker.EntityMemberChanging(string entityMemberName)
267 this.EntityMemberChanging(entityMemberName);
271 /// Used to report that a scalar entity property has been changed
272 /// The property value that was cached during EntityMemberChanging is now
273 /// added to OriginalValues
275 /// <param name="entityMemberName">The name of the entity property that has changing</param>
276 void IEntityChangeTracker.EntityMemberChanged(string entityMemberName)
278 this.EntityMemberChanged(entityMemberName);
282 /// Used to report that a complex property is about to change
283 /// The current value of the specified property is cached when this method is called.
285 /// <param name="entityMemberName">The name of the top-level entity property that is changing</param>
286 /// <param name="complexObject">The complex object that contains the property that is changing</param>
287 /// <param name="complexObjectMemberName">The name of the property that is changing on complexObject</param>
288 void IEntityChangeTracker.EntityComplexMemberChanging(string entityMemberName, object complexObject, string complexObjectMemberName)
290 this.EntityComplexMemberChanging(entityMemberName, complexObject, complexObjectMemberName);
294 /// Used to report that a complex property has been changed
295 /// The property value that was cached during EntityMemberChanging is now added to OriginalValues
297 /// <param name="entityMemberName">The name of the top-level entity property that has changed</param>
298 /// <param name="complexObject">The complex object that contains the property that changed</param>
299 /// <param name="complexObjectMemberName">The name of the property that changed on complexObject</param>
300 void IEntityChangeTracker.EntityComplexMemberChanged(string entityMemberName, object complexObject, string complexObjectMemberName)
302 this.EntityComplexMemberChanged(entityMemberName, complexObject, complexObjectMemberName);
306 /// Returns the EntityState from the ObjectStateEntry
308 EntityState IEntityChangeTracker.EntityState
316 #endregion // IEntityChangeTracker
318 #region Internal members
320 abstract internal bool IsKeyEntry { get; }
322 abstract internal int GetFieldCount(StateManagerTypeMetadata metadata);
324 abstract internal Type GetFieldType(int ordinal, StateManagerTypeMetadata metadata);
326 abstract internal string GetCLayerName(int ordinal, StateManagerTypeMetadata metadata);
328 abstract internal int GetOrdinalforCLayerName(string name, StateManagerTypeMetadata metadata);
330 abstract internal void RevertDelete();
332 abstract internal void SetModifiedAll();
334 abstract internal void EntityMemberChanging(string entityMemberName);
335 abstract internal void EntityMemberChanged(string entityMemberName);
336 abstract internal void EntityComplexMemberChanging(string entityMemberName, object complexObject, string complexObjectMemberName);
337 abstract internal void EntityComplexMemberChanged(string entityMemberName, object complexObject, string complexObjectMemberName);
340 /// Reuse or create a new (Entity)DataRecordInfo.
342 abstract internal DataRecordInfo GetDataRecordInfo(StateManagerTypeMetadata metadata, object userObject);
344 virtual internal void Reset()
348 _state = EntityState.Detached;
351 internal void ValidateState()
353 if (_state == EntityState.Detached)
355 throw EntityUtil.ObjectStateEntryinInvalidState();
357 Debug.Assert(null != _cache, "null ObjectStateManager");
358 Debug.Assert(null != _entitySet, "null EntitySetBase");
361 #endregion // Internal members
364 internal struct StateManagerValue
366 internal StateManagerMemberMetadata memberMetadata;
367 internal object userObject;
368 internal object originalValue;
370 internal StateManagerValue(StateManagerMemberMetadata metadata, object instance, object value)
372 memberMetadata = metadata;
373 userObject = instance;
374 originalValue = value;
378 internal enum ObjectStateValueRecord
380 OriginalReadonly = 0,
381 CurrentUpdatable = 1,
382 OriginalUpdatableInternal = 2,
383 OriginalUpdatablePublic = 3,
387 // This class is used in Referential Integrity Constraints feature.
388 // It is used to get around the problem of enumerating dictionary contents,
389 // but allowing update of the value without breaking the enumerator.
390 internal sealed class IntBox
394 internal IntBox(int val)