1 //--------------------------------------------------------------------- 2 // <copyright file="ObjectStateEntry.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 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; 15 16 namespace System.Data.Objects 17 { 18 // Detached - nothing 19 20 // Added - _entity & _currentValues only for shadowState 21 22 // Unchanged - _entity & _currentValues only for shadowState 23 // Unchanged -> Deleted - _entity & _currentValues only for shadowState 24 25 // Modified - _currentValues & _modifiedFields + _originalValues only on change 26 // Modified -> Deleted - _currentValues & _modifiedFields + _originalValues only on change 27 28 /// <summary> 29 /// Represets either a entity, entity stub or relationship 30 /// </summary> 31 public abstract class ObjectStateEntry : IEntityStateEntry, IEntityChangeTracker 32 { 33 #region common entry fields 34 internal ObjectStateManager _cache; 35 internal EntitySetBase _entitySet; 36 internal EntityState _state; 37 #endregion 38 39 #region Constructor 40 // ObjectStateEntry will not be detached and creation will be handled from ObjectStateManager ObjectStateEntry(ObjectStateManager cache, EntitySet entitySet, EntityState state)41 internal ObjectStateEntry(ObjectStateManager cache, EntitySet entitySet, EntityState state) 42 { 43 Debug.Assert(cache != null, "cache cannot be null."); 44 45 _cache = cache; 46 _entitySet = entitySet; 47 _state = state; 48 } 49 #endregion // Constructor 50 51 #region Public members 52 /// <summary> 53 /// ObjectStateManager property of ObjectStateEntry. 54 /// </summary> 55 /// <param></param> 56 /// <returns> ObjectStateManager </returns> 57 public ObjectStateManager ObjectStateManager 58 { 59 get 60 { 61 ValidateState(); 62 return _cache; 63 } 64 } 65 66 /// <summary> Extent property of ObjectStateEntry. </summary> 67 /// <param></param> 68 /// <returns> Extent </returns> 69 public EntitySetBase EntitySet 70 { 71 get 72 { 73 ValidateState(); 74 return _entitySet; 75 } 76 } 77 78 /// <summary> 79 /// State property of ObjectStateEntry. 80 /// </summary> 81 /// <param></param> 82 /// <returns> DataRowState </returns> 83 public EntityState State 84 { 85 get 86 { 87 return _state; 88 } 89 internal set 90 { 91 _state = value; 92 } 93 } 94 95 /// <summary> 96 /// Entity property of ObjectStateEntry. 97 /// </summary> 98 /// <param></param> 99 /// <returns> The entity encapsulated by this entry. </returns> 100 abstract public object Entity { get; } 101 102 /// <summary> 103 /// The EntityKey associated with the ObjectStateEntry 104 /// </summary> 105 abstract public EntityKey EntityKey { get; internal set; } 106 107 /// <summary> 108 /// Determines if this ObjectStateEntry represents a relationship 109 /// </summary> 110 abstract public bool IsRelationship { get; } 111 112 /// <summary> 113 /// Gets bit array indicating which properties are modified. 114 /// </summary> 115 abstract internal BitArray ModifiedProperties { get; } 116 117 BitArray IEntityStateEntry.ModifiedProperties { get { return this.ModifiedProperties; } } 118 119 /// <summary> 120 /// Original values of entity 121 /// </summary> 122 /// <param></param> 123 /// <returns> DbDataRecord </returns> 124 [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this 125 abstract public DbDataRecord OriginalValues { get; } 126 GetUpdatableOriginalValues()127 abstract public OriginalValueRecord GetUpdatableOriginalValues(); 128 129 /// <summary> 130 /// Current values of entity/ DataRow 131 /// </summary> 132 /// <param></param> 133 /// <returns> DbUpdatableDataRecord </returns> 134 [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this 135 abstract public CurrentValueRecord CurrentValues { get; } 136 137 /// <summary> 138 /// API to accept the current values as original values and mark the entity as Unchanged. 139 /// </summary> 140 /// <param></param> 141 /// <returns></returns> AcceptChanges()142 abstract public void AcceptChanges(); 143 144 /// <summary> 145 /// API to mark the entity deleted. if entity is in added state, it will be detached 146 /// </summary> 147 /// <param></param> 148 /// <returns> </returns> Delete()149 abstract public void Delete(); 150 151 /// <summary> 152 /// API to return properties that are marked modified 153 /// </summary> 154 /// <param> </param> 155 /// <returns> IEnumerable of modified properties names, names are in term of c-space </returns> GetModifiedProperties()156 abstract public IEnumerable<string> GetModifiedProperties(); 157 158 /// <summary> 159 /// set the state to Modified. 160 /// </summary> 161 /// <param></param> 162 /// <returns></returns> 163 /// <exception cref="InvalidOperationException">If State is not Modified or Unchanged</exception> 164 /// SetModified()165 abstract public void SetModified(); 166 167 /// <summary> 168 /// Marks specified property as modified. 169 /// </summary> 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> 172 /// SetModifiedProperty(string propertyName)173 abstract public void SetModifiedProperty(string propertyName); 174 175 /// <summary> 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. 179 /// </summary> 180 /// <remarks> 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 187 /// is a no-op. 188 /// </remarks> 189 /// <param name="propertyName">The name of the property to change.</param> RejectPropertyChanges(string propertyName)190 abstract public void RejectPropertyChanges(string propertyName); 191 192 /// <summary> 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. 196 /// </summary> 197 /// <remarks> 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. 200 /// </remarks> 201 /// <param name="propertyName">The name of the property.</param> 202 /// <returns>True if the property has changed; false otherwise.</returns> IsPropertyChanged(string propertyName)203 abstract public bool IsPropertyChanged(string propertyName); 204 205 /// <summary> 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. 210 /// </summary> 211 /// <exception cref="InvalidOperationException">The entry is a stub or represents a relationship</exception> 212 abstract public RelationshipManager RelationshipManager 213 { 214 get; 215 } 216 217 /// <summary> 218 /// Changes state of the entry to the specified <paramref name="state"/> 219 /// </summary> 220 /// <param name="state">The requested state</param> ChangeState(EntityState state)221 abstract public void ChangeState(EntityState state); 222 223 /// <summary> 224 /// Apply modified properties to the original object. 225 /// </summary> 226 /// <param name="current">object with modified properties</param> ApplyCurrentValues(object currentEntity)227 abstract public void ApplyCurrentValues(object currentEntity); 228 229 /// <summary> 230 /// Apply original values to the entity. 231 /// </summary> 232 /// <param name="original">The object with original values</param> ApplyOriginalValues(object originalEntity)233 abstract public void ApplyOriginalValues(object originalEntity); 234 235 #endregion // Public members 236 237 #region IEntityStateEntry 238 IEntityStateManager IEntityStateEntry.StateManager 239 { 240 get 241 { 242 return (IEntityStateManager)this.ObjectStateManager; 243 } 244 } 245 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 250 { 251 get 252 { 253 return this.IsKeyEntry; 254 } 255 } 256 #endregion // IEntityStateEntry 257 258 #region Public IEntityChangeTracker 259 260 /// <summary> 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. 263 /// </summary> 264 /// <param name="entityMemberName">The name of the entity property that is changing</param> IEntityChangeTracker.EntityMemberChanging(string entityMemberName)265 void IEntityChangeTracker.EntityMemberChanging(string entityMemberName) 266 { 267 this.EntityMemberChanging(entityMemberName); 268 } 269 270 /// <summary> 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 274 /// </summary> 275 /// <param name="entityMemberName">The name of the entity property that has changing</param> IEntityChangeTracker.EntityMemberChanged(string entityMemberName)276 void IEntityChangeTracker.EntityMemberChanged(string entityMemberName) 277 { 278 this.EntityMemberChanged(entityMemberName); 279 } 280 281 /// <summary> 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. 284 /// </summary> 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> IEntityChangeTracker.EntityComplexMemberChanging(string entityMemberName, object complexObject, string complexObjectMemberName)288 void IEntityChangeTracker.EntityComplexMemberChanging(string entityMemberName, object complexObject, string complexObjectMemberName) 289 { 290 this.EntityComplexMemberChanging(entityMemberName, complexObject, complexObjectMemberName); 291 } 292 293 /// <summary> 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 296 /// </summary> 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> IEntityChangeTracker.EntityComplexMemberChanged(string entityMemberName, object complexObject, string complexObjectMemberName)300 void IEntityChangeTracker.EntityComplexMemberChanged(string entityMemberName, object complexObject, string complexObjectMemberName) 301 { 302 this.EntityComplexMemberChanged(entityMemberName, complexObject, complexObjectMemberName); 303 } 304 305 /// <summary> 306 /// Returns the EntityState from the ObjectStateEntry 307 /// </summary> 308 EntityState IEntityChangeTracker.EntityState 309 { 310 get 311 { 312 return this.State; 313 } 314 } 315 316 #endregion // IEntityChangeTracker 317 318 #region Internal members 319 320 abstract internal bool IsKeyEntry { get; } 321 GetFieldCount(StateManagerTypeMetadata metadata)322 abstract internal int GetFieldCount(StateManagerTypeMetadata metadata); 323 GetFieldType(int ordinal, StateManagerTypeMetadata metadata)324 abstract internal Type GetFieldType(int ordinal, StateManagerTypeMetadata metadata); 325 GetCLayerName(int ordinal, StateManagerTypeMetadata metadata)326 abstract internal string GetCLayerName(int ordinal, StateManagerTypeMetadata metadata); 327 GetOrdinalforCLayerName(string name, StateManagerTypeMetadata metadata)328 abstract internal int GetOrdinalforCLayerName(string name, StateManagerTypeMetadata metadata); 329 RevertDelete()330 abstract internal void RevertDelete(); 331 SetModifiedAll()332 abstract internal void SetModifiedAll(); 333 EntityMemberChanging(string entityMemberName)334 abstract internal void EntityMemberChanging(string entityMemberName); EntityMemberChanged(string entityMemberName)335 abstract internal void EntityMemberChanged(string entityMemberName); EntityComplexMemberChanging(string entityMemberName, object complexObject, string complexObjectMemberName)336 abstract internal void EntityComplexMemberChanging(string entityMemberName, object complexObject, string complexObjectMemberName); EntityComplexMemberChanged(string entityMemberName, object complexObject, string complexObjectMemberName)337 abstract internal void EntityComplexMemberChanged(string entityMemberName, object complexObject, string complexObjectMemberName); 338 339 /// <summary> 340 /// Reuse or create a new (Entity)DataRecordInfo. 341 /// </summary> GetDataRecordInfo(StateManagerTypeMetadata metadata, object userObject)342 abstract internal DataRecordInfo GetDataRecordInfo(StateManagerTypeMetadata metadata, object userObject); 343 Reset()344 virtual internal void Reset() 345 { 346 _cache = null; 347 _entitySet = null; 348 _state = EntityState.Detached; 349 } 350 ValidateState()351 internal void ValidateState() 352 { 353 if (_state == EntityState.Detached) 354 { 355 throw EntityUtil.ObjectStateEntryinInvalidState(); 356 } 357 Debug.Assert(null != _cache, "null ObjectStateManager"); 358 Debug.Assert(null != _entitySet, "null EntitySetBase"); 359 } 360 361 #endregion // Internal members 362 } 363 364 internal struct StateManagerValue 365 { 366 internal StateManagerMemberMetadata memberMetadata; 367 internal object userObject; 368 internal object originalValue; 369 StateManagerValueSystem.Data.Objects.StateManagerValue370 internal StateManagerValue(StateManagerMemberMetadata metadata, object instance, object value) 371 { 372 memberMetadata = metadata; 373 userObject = instance; 374 originalValue = value; 375 } 376 } 377 378 internal enum ObjectStateValueRecord 379 { 380 OriginalReadonly = 0, 381 CurrentUpdatable = 1, 382 OriginalUpdatableInternal = 2, 383 OriginalUpdatablePublic = 3, 384 } 385 386 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 391 { 392 private int val; 393 IntBox(int val)394 internal IntBox(int val) 395 { 396 this.val = val; 397 } 398 399 internal int Value 400 { 401 get 402 { 403 return val; 404 } 405 406 set 407 { 408 val = value; 409 } 410 } 411 } 412 } 413