1 //---------------------------------------------------------------------
2 // <copyright file="EntityObject.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 using System.Diagnostics;
11 using System.Reflection;
12 using System.ComponentModel;
13 using System.Runtime.Serialization;
15 namespace System.Data.Objects.DataClasses
18 /// This is the class is the basis for all perscribed EntityObject classes.
20 [DataContract(IsReference=true)]
22 public abstract class EntityObject : StructuralObject, IEntityWithKey, IEntityWithChangeTracker, IEntityWithRelationships
26 // The following 2 fields are serialized. Adding or removing a serialized field is considered
27 // a breaking change. This includes changing the field type or field name of existing
28 // serialized fields. If you need to make this kind of change, it may be possible, but it
29 // will require some custom serialization/deserialization code.
30 private RelationshipManager _relationships;
31 private EntityKey _entityKey;
34 private IEntityChangeTracker _entityChangeTracker = s_detachedEntityChangeTracker;
36 private static readonly DetachedEntityChangeTracker s_detachedEntityChangeTracker = new DetachedEntityChangeTracker();
39 /// Helper class used when we are not currently attached to a change tracker.
40 /// Simplifies the code so we don't always have to check for null before using the change tracker
42 private class DetachedEntityChangeTracker : IEntityChangeTracker
44 void IEntityChangeTracker.EntityMemberChanging(string entityMemberName) { }
45 void IEntityChangeTracker.EntityMemberChanged(string entityMemberName) { }
46 void IEntityChangeTracker.EntityComplexMemberChanging(string entityMemberName, object complexObject, string complexMemberName) { }
47 void IEntityChangeTracker.EntityComplexMemberChanged(string entityMemberName, object complexObject, string complexMemberName) { }
48 EntityState IEntityChangeTracker.EntityState
52 return EntityState.Detached;
57 private IEntityChangeTracker EntityChangeTracker
61 if (_entityChangeTracker == null)
63 _entityChangeTracker = s_detachedEntityChangeTracker;
65 return _entityChangeTracker;
69 _entityChangeTracker = value;
76 /// The storage state of this EntityObject
79 /// This property returns a value from the EntityState enum.
81 [System.ComponentModel.Browsable(false)]
82 [System.Xml.Serialization.XmlIgnore]
83 public EntityState EntityState
87 Debug.Assert(EntityChangeTracker != null,
88 "EntityChangeTracker should never return null -- if detached should be set to s_detachedEntityChangeTracker");
89 Debug.Assert(EntityChangeTracker != s_detachedEntityChangeTracker ? EntityChangeTracker.EntityState != EntityState.Detached : true,
90 "Should never get a detached state from an attached change tracker.");
92 return EntityChangeTracker.EntityState;
96 #region IEntityWithKey
99 /// Returns the EntityKey for this EntityObject.
103 public EntityKey EntityKey
111 // Report the change to the change tracker
112 // If we are not attached to a change tracker, we can do anything we want to the key
113 // If we are attached, the change tracker should make sure the new value is valid for the current state
114 Debug.Assert(EntityChangeTracker != null, "_entityChangeTracker should never be null -- if detached it should return s_detachedEntityChangeTracker");
115 EntityChangeTracker.EntityMemberChanging(StructuralObject.EntityKeyPropertyName);
117 EntityChangeTracker.EntityMemberChanged(StructuralObject.EntityKeyPropertyName);
122 #region IEntityWithChangeTracker
126 /// Used by the ObjectStateManager to attach or detach this EntityObject to the cache.
128 /// <param name="changeTracker">
129 /// Reference to the ObjectStateEntry that contains this entity
131 void IEntityWithChangeTracker.SetChangeTracker(IEntityChangeTracker changeTracker)
133 // Fail if the change tracker is already set for this EntityObject and it's being set to something different
134 // If the original change tracker is associated with a disposed ObjectStateManager, then allow
135 // the entity to be attached
136 if (changeTracker != null && EntityChangeTracker != s_detachedEntityChangeTracker && !Object.ReferenceEquals(changeTracker, EntityChangeTracker))
138 EntityEntry entry = EntityChangeTracker as EntityEntry;
139 if (entry == null || !entry.ObjectStateManager.IsDisposed)
141 throw EntityUtil.EntityCantHaveMultipleChangeTrackers();
145 EntityChangeTracker = changeTracker;
148 #endregion IEntityWithChangeTracker
149 #region IEntityWithRelationships
152 /// Returns the container for the lazily created relationship
153 /// navigation property objects, collections and refs.
155 RelationshipManager IEntityWithRelationships.RelationshipManager
159 if (_relationships == null)
161 _relationships = RelationshipManager.Create(this);
164 return _relationships;
170 #region Protected Change Tracking Methods
173 /// This method is called whenever a change is going to be made to an EntityObject
176 /// <param name="property">
177 /// The name of the changing property.
179 /// <exception cref="System.ArgumentNullException">
180 /// When parameter member is null (Nothing in Visual Basic).
182 protected sealed override void ReportPropertyChanging(
185 EntityUtil.CheckStringArgument(property, "property");
187 Debug.Assert(EntityChangeTracker != null, "_entityChangeTracker should never be null -- if detached it should return s_detachedEntityChangeTracker");
189 base.ReportPropertyChanging(property);
191 EntityChangeTracker.EntityMemberChanging(property);
195 /// This method is called whenever a change is made to an EntityObject
198 /// <param name="property">
199 /// The name of the changed property.
201 /// <exception cref="System.ArgumentNullException">
202 /// When parameter member is null (Nothing in Visual Basic).
204 protected sealed override void ReportPropertyChanged(
207 EntityUtil.CheckStringArgument(property, "property");
209 Debug.Assert(EntityChangeTracker != null, "EntityChangeTracker should never return null -- if detached it should be return s_detachedEntityChangeTracker");
210 EntityChangeTracker.EntityMemberChanged(property);
212 base.ReportPropertyChanged(property);
216 #region Internal ComplexObject Change Tracking Methods and Properties
218 internal sealed override bool IsChangeTracked
222 return EntityState != EntityState.Detached;
227 /// This method is called by a ComplexObject contained in this Entity
228 /// whenever a change is about to be made to a property of the
229 /// ComplexObject so that the change can be forwarded to the change tracker.
231 /// <param name="entityMemberName">
232 /// The name of the top-level entity property that contains the ComplexObject that is calling this method.
234 /// <param name="complexObject">
235 /// The instance of the ComplexObject on which the property is changing.
237 /// <param name="complexMemberName">
238 /// The name of the changing property on complexObject.
240 internal sealed override void ReportComplexPropertyChanging(
241 string entityMemberName, ComplexObject complexObject, string complexMemberName)
243 Debug.Assert(complexObject != null, "invalid complexObject");
244 Debug.Assert(!String.IsNullOrEmpty(complexMemberName), "invalid complexMemberName");
246 EntityChangeTracker.EntityComplexMemberChanging(entityMemberName, complexObject, complexMemberName);
250 /// This method is called by a ComplexObject contained in this Entity
251 /// whenever a change has been made to a property of the
252 /// ComplexObject so that the change can be forwarded to the change tracker.
254 /// <param name="entityMemberName">
255 /// The name of the top-level entity property that contains the ComplexObject that is calling this method.
257 /// <param name="complexObject">
258 /// The instance of the ComplexObject on which the property is changing.
260 /// <param name="complexMemberName">
261 /// The name of the changing property on complexObject.
263 internal sealed override void ReportComplexPropertyChanged(
264 string entityMemberName, ComplexObject complexObject, string complexMemberName)
266 Debug.Assert(complexObject != null, "invalid complexObject");
267 Debug.Assert(!String.IsNullOrEmpty(complexMemberName), "invalid complexMemberName");
269 EntityChangeTracker.EntityComplexMemberChanged(entityMemberName, complexObject, complexMemberName);