Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Objects / DataClasses / EntityObject.cs
1 //---------------------------------------------------------------------
2 // <copyright file="EntityObject.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 using System.Data;
10 using System.Diagnostics;
11 using System.Reflection;
12 using System.ComponentModel;
13 using System.Runtime.Serialization;
14
15 namespace System.Data.Objects.DataClasses
16 {
17     /// <summary>
18     /// This is the class is the basis for all perscribed EntityObject classes.
19     /// </summary>
20     [DataContract(IsReference=true)]
21     [Serializable]
22     public abstract class EntityObject : StructuralObject, IEntityWithKey, IEntityWithChangeTracker, IEntityWithRelationships
23     {
24         #region Privates
25
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;
32
33         [NonSerialized]
34         private IEntityChangeTracker _entityChangeTracker = s_detachedEntityChangeTracker;
35         [NonSerialized]
36         private static readonly DetachedEntityChangeTracker s_detachedEntityChangeTracker = new DetachedEntityChangeTracker();
37
38         /// <summary>
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
41         /// </summary>
42         private class DetachedEntityChangeTracker : IEntityChangeTracker
43         {
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
49             {
50                 get
51                 {
52                     return EntityState.Detached;
53                 }
54             }
55         }
56
57         private IEntityChangeTracker EntityChangeTracker
58         {
59             get
60             {
61                 if (_entityChangeTracker == null)
62                 {
63                     _entityChangeTracker = s_detachedEntityChangeTracker;
64                 }
65                 return _entityChangeTracker;
66             }
67             set
68             {
69                 _entityChangeTracker = value;
70             }
71         }
72
73         #endregion
74         #region Publics
75         /// <summary>
76         /// The storage state of this EntityObject 
77         /// </summary>
78         /// <value>
79         /// This property returns a value from the EntityState enum.
80         /// </value>
81         [System.ComponentModel.Browsable(false)]
82         [System.Xml.Serialization.XmlIgnore]
83         public EntityState EntityState
84         {
85             get
86             {
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.");
91
92                 return EntityChangeTracker.EntityState;
93             }
94         }
95
96         #region IEntityWithKey
97
98         /// <summary>
99         /// Returns the EntityKey for this EntityObject.
100         /// </summary>
101         [Browsable(false)]
102         [DataMember]
103         public EntityKey EntityKey
104         {
105             get
106             {
107                 return _entityKey;
108             }
109             set
110             {
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);
116                 _entityKey = value;
117                 EntityChangeTracker.EntityMemberChanged(StructuralObject.EntityKeyPropertyName);                
118             }
119         } 
120
121         #endregion       
122         #region IEntityWithChangeTracker
123
124
125         /// <summary>
126         /// Used by the ObjectStateManager to attach or detach this EntityObject to the cache.
127         /// </summary>
128         /// <param name="changeTracker">
129         /// Reference to the ObjectStateEntry that contains this entity
130         /// </param>
131         void IEntityWithChangeTracker.SetChangeTracker(IEntityChangeTracker changeTracker)
132         {
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))
137             {
138                 EntityEntry entry = EntityChangeTracker as EntityEntry;
139                 if (entry == null || !entry.ObjectStateManager.IsDisposed)
140                 {
141                     throw EntityUtil.EntityCantHaveMultipleChangeTrackers();
142                 }
143             }
144             
145             EntityChangeTracker = changeTracker;
146         }
147
148         #endregion IEntityWithChangeTracker
149         #region IEntityWithRelationships
150
151         /// <summary>
152         /// Returns the container for the lazily created relationship 
153         /// navigation property objects, collections and refs.
154         /// </summary>
155         RelationshipManager IEntityWithRelationships.RelationshipManager
156         {
157             get
158             {
159                 if (_relationships == null)
160                 {
161                     _relationships = RelationshipManager.Create(this);
162                 }
163
164                 return _relationships;
165             }
166         }
167
168         #endregion
169         #endregion        
170         #region Protected Change Tracking Methods
171
172         /// <summary>
173         /// This method is called whenever a change is going to be made to an EntityObject 
174         /// property.
175         /// </summary>
176         /// <param name="property">
177         /// The name of the changing property.
178         /// </param>        
179         /// <exception cref="System.ArgumentNullException">
180         /// When parameter member is null (Nothing in Visual Basic).
181         /// </exception>
182         protected sealed override void ReportPropertyChanging(
183             string property)
184         {
185             EntityUtil.CheckStringArgument(property, "property");
186
187             Debug.Assert(EntityChangeTracker != null, "_entityChangeTracker should never be null -- if detached it should return s_detachedEntityChangeTracker");
188
189             base.ReportPropertyChanging(property);
190
191             EntityChangeTracker.EntityMemberChanging(property);
192         }
193
194         /// <summary>
195         /// This method is called whenever a change is made to an EntityObject 
196         /// property.
197         /// </summary>
198         /// <param name="property">
199         /// The name of the changed property.
200         /// </param>        
201         /// <exception cref="System.ArgumentNullException">
202         /// When parameter member is null (Nothing in Visual Basic).
203         /// </exception>
204         protected sealed override void ReportPropertyChanged(
205             string property)
206         {
207             EntityUtil.CheckStringArgument(property, "property");
208
209             Debug.Assert(EntityChangeTracker != null, "EntityChangeTracker should never return null -- if detached it should be return s_detachedEntityChangeTracker");
210             EntityChangeTracker.EntityMemberChanged(property);           
211             
212             base.ReportPropertyChanged(property);
213         }
214
215         #endregion
216         #region Internal ComplexObject Change Tracking Methods and Properties
217
218         internal sealed override bool IsChangeTracked
219         {
220             get
221             {
222                 return EntityState != EntityState.Detached;
223             }
224         }
225
226         /// <summary>
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.
230         /// </summary>
231         /// <param name="entityMemberName">
232         /// The name of the top-level entity property that contains the ComplexObject that is calling this method.
233         /// </param>
234         /// <param name="complexObject">
235         /// The instance of the ComplexObject on which the property is changing.
236         /// </param>
237         /// <param name="complexMemberName">
238         /// The name of the changing property on complexObject.
239         /// </param>        
240         internal sealed override void ReportComplexPropertyChanging(
241             string entityMemberName, ComplexObject complexObject, string complexMemberName)
242         {
243             Debug.Assert(complexObject != null, "invalid complexObject");
244             Debug.Assert(!String.IsNullOrEmpty(complexMemberName), "invalid complexMemberName");
245
246             EntityChangeTracker.EntityComplexMemberChanging(entityMemberName, complexObject, complexMemberName);            
247         }
248
249         /// <summary>
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.
253         /// </summary>
254         /// <param name="entityMemberName">
255         /// The name of the top-level entity property that contains the ComplexObject that is calling this method.
256         /// </param>
257         /// <param name="complexObject">
258         /// The instance of the ComplexObject on which the property is changing.
259         /// </param>
260         /// <param name="complexMemberName">
261         /// The name of the changing property on complexObject.
262         /// </param>        
263         internal sealed override void ReportComplexPropertyChanged(
264             string entityMemberName, ComplexObject complexObject, string complexMemberName)
265         {
266             Debug.Assert(complexObject != null, "invalid complexObject");
267             Debug.Assert(!String.IsNullOrEmpty(complexMemberName), "invalid complexMemberName");
268
269             EntityChangeTracker.EntityComplexMemberChanged(entityMemberName, complexObject, complexMemberName);
270         }
271
272         #endregion      
273     }
274 }
275