1 //---------------------------------------------------------------------
2 // <copyright file="BaseEntityWrapper.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;
10 using System.Data.Objects.DataClasses;
11 using System.Diagnostics;
12 using System.Reflection;
13 using System.Data.Metadata.Edm;
14 
15 namespace System.Data.Objects.Internal
16 {
17     /// <summary>
18     /// Base class containing common code for different implementations of the IEntityWrapper
19     /// interface.  Generally speaking, operations involving the ObjectContext, RelationshipManager
20     /// and raw Entity are handled through this class.
21     /// </summary>
22     /// <typeparam name="TEntity">The type of entity wrapped</typeparam>
23     internal abstract class BaseEntityWrapper<TEntity> : IEntityWrapper
24     {
25         // This enum allows boolean flags to be added to the wrapper without introducing a new field
26         // for each one.  This helps keep the wrapper memory footprint small, which is important
27         // in some high-performance NoTracking cases.
28         [Flags]
29         private enum WrapperFlags
30         {
31             None = 0,
32             NoTracking = 1,
33             InitializingRelatedEnds = 2,
34         }
35 
36         private readonly RelationshipManager _relationshipManager;
37         private Type _identityType;
38         private WrapperFlags _flags;
39 
40         /// <summary>
41         /// Constructs a wrapper for the given entity and its associated RelationshipManager.
42         /// </summary>
43         /// <param name="entity">The entity to be wrapped</param>
44         /// <param name="relationshipManager">the RelationshipManager associated with this entity</param>
BaseEntityWrapper(TEntity entity, RelationshipManager relationshipManager)45         protected BaseEntityWrapper(TEntity entity, RelationshipManager relationshipManager)
46         {
47             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
48             Debug.Assert(entity != null, "Factory should ensure wrapped entity here is never null.");
49             if (relationshipManager == null)
50             {
51                 throw EntityUtil.UnexpectedNullRelationshipManager();
52             }
53             _relationshipManager = relationshipManager;
54         }
55 
56         /// <summary>
57         /// Constructs a wrapper as part of the materialization process.  This constructor is only used
58         /// during materialization where it is known that the entity being wrapped is newly constructed.
59         /// This means that some checks are not performed that might be needed when thw wrapper is
60         /// created at other times, and information such as the identity type is passed in because
61         /// it is readily available in the materializer.
62         /// </summary>
63         /// <param name="entity">The entity to wrap</param>
64         /// <param name="relationshipManager">The RelationshipManager associated with this entity</param>
65         /// <param name="entitySet">The entity set, or null if none is known</param>
66         /// <param name="context">The context to which the entity should be attached</param>
67         /// <param name="mergeOption">NoTracking for non-tracked entities, AppendOnly otherwise</param>
68         /// <param name="identityType">The type of the entity ignoring any possible proxy type</param>
BaseEntityWrapper(TEntity entity, RelationshipManager relationshipManager, EntitySet entitySet, ObjectContext context, MergeOption mergeOption, Type identityType)69         protected BaseEntityWrapper(TEntity entity, RelationshipManager relationshipManager, EntitySet entitySet, ObjectContext context, MergeOption mergeOption, Type identityType)
70         {
71             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
72             Debug.Assert(entity != null, "Factory should ensure wrapped entity here is never null.");
73             if (relationshipManager == null)
74             {
75                 throw EntityUtil.UnexpectedNullRelationshipManager();
76             }
77             _identityType = identityType;
78             _relationshipManager = relationshipManager;
79             RelationshipManager.SetWrappedOwner(this, entity);
80             if (entitySet != null)
81             {
82                 Context = context;
83                 MergeOption = mergeOption;
84                 RelationshipManager.AttachContextToRelatedEnds(context, entitySet, mergeOption);
85             }
86 
87         }
88 
89         // See IEntityWrapper documentation
90         public RelationshipManager RelationshipManager
91         {
92             get
93             {
94                 return _relationshipManager;
95             }
96         }
97 
98         // See IEntityWrapper documentation
99         public ObjectContext Context
100         {
101             get;
102             set;
103         }
104 
105         // See IEntityWrapper documentation
106         public MergeOption MergeOption
107         {
108             get
109             {
110                 return (_flags & WrapperFlags.NoTracking) != 0 ? MergeOption.NoTracking : MergeOption.AppendOnly;
111             }
112             private set
113             {
114                 Debug.Assert(value == MergeOption.AppendOnly || value == MergeOption.NoTracking, "Merge option must be one of NoTracking or AppendOnly.");
115                 if (value == MergeOption.NoTracking)
116                 {
117                     _flags |= WrapperFlags.NoTracking;
118                 }
119                 else
120                 {
121                     _flags &= ~WrapperFlags.NoTracking;
122                 }
123             }
124         }
125 
126         // See IEntityWrapper documentation
127         public bool InitializingProxyRelatedEnds
128         {
129             get
130             {
131                 return (_flags & WrapperFlags.InitializingRelatedEnds) != 0;
132             }
133             set
134             {
135                 if (value)
136                 {
137                     _flags |= WrapperFlags.InitializingRelatedEnds;
138                 }
139                 else
140                 {
141                     _flags &= ~WrapperFlags.InitializingRelatedEnds;
142                 }
143             }
144         }
145 
146         // See IEntityWrapper documentation
AttachContext(ObjectContext context, EntitySet entitySet, MergeOption mergeOption)147         public void AttachContext(ObjectContext context, EntitySet entitySet, MergeOption mergeOption)
148         {
149             Debug.Assert(null != context, "context");
150             Context = context;
151             MergeOption = mergeOption;
152             if (entitySet != null)
153             {
154                 RelationshipManager.AttachContextToRelatedEnds(context, entitySet, mergeOption);
155             }
156         }
157 
158         // See IEntityWrapper documentation
ResetContext(ObjectContext context, EntitySet entitySet, MergeOption mergeOption)159         public void ResetContext(ObjectContext context, EntitySet entitySet, MergeOption mergeOption)
160         {
161             Debug.Assert(null != entitySet, "entitySet should not be null");
162             Debug.Assert(null != context, "context");
163             Debug.Assert(MergeOption.NoTracking == mergeOption ||
164                          MergeOption.AppendOnly == mergeOption,
165                          "mergeOption");
166 
167             if (!object.ReferenceEquals(Context, context))
168             {
169                 Context = context;
170                 MergeOption = mergeOption;
171                 RelationshipManager.ResetContextOnRelatedEnds(context, entitySet, mergeOption);
172             }
173         }
174 
175         // See IEntityWrapper documentation
DetachContext()176         public void DetachContext()
177         {
178             if (Context != null &&
179                 Context.ObjectStateManager.TransactionManager.IsAttachTracking &&
180                 Context.ObjectStateManager.TransactionManager.OriginalMergeOption == MergeOption.NoTracking)
181             {
182                 // If AttachTo() failed while attaching graph retrieved with NoTracking option,
183                 // we don't want to clear the Context property of the wrapped entity
184                 MergeOption = MergeOption.NoTracking;
185             }
186             else
187             {
188                 Context = null;
189             }
190 
191             RelationshipManager.DetachContextFromRelatedEnds();
192         }
193 
194         // See IEntityWrapper documentation
195         public EntityEntry ObjectStateEntry
196         {
197             get;
198             set;
199         }
200 
201         // See IEntityWrapper documentation
202         public Type IdentityType
203         {
204             get
205             {
206                 if (_identityType == null)
207                 {
208                     _identityType = EntityUtil.GetEntityIdentityType(typeof(TEntity));
209                 }
210                 return _identityType;
211             }
212         }
213 
214         // All these methods defined by IEntityWrapper
EnsureCollectionNotNull(RelatedEnd relatedEnd)215         public abstract void EnsureCollectionNotNull(RelatedEnd relatedEnd);
216         public abstract EntityKey EntityKey { get; set; }
217         public abstract bool OwnsRelationshipManager
218         {
219             get;
220         }
GetEntityKeyFromEntity()221         public abstract EntityKey GetEntityKeyFromEntity();
SetChangeTracker(IEntityChangeTracker changeTracker)222         public abstract void SetChangeTracker(IEntityChangeTracker changeTracker);
TakeSnapshot(EntityEntry entry)223         public abstract void TakeSnapshot(EntityEntry entry);
TakeSnapshotOfRelationships(EntityEntry entry)224         public abstract void TakeSnapshotOfRelationships(EntityEntry entry);
GetNavigationPropertyValue(RelatedEnd relatedEnd)225         public abstract object GetNavigationPropertyValue(RelatedEnd relatedEnd);
SetNavigationPropertyValue(RelatedEnd relatedEnd, object value)226         public abstract void SetNavigationPropertyValue(RelatedEnd relatedEnd, object value);
RemoveNavigationPropertyValue(RelatedEnd relatedEnd, object value)227         public abstract void RemoveNavigationPropertyValue(RelatedEnd relatedEnd, object value);
CollectionAdd(RelatedEnd relatedEnd, object value)228         public abstract void CollectionAdd(RelatedEnd relatedEnd, object value);
CollectionRemove(RelatedEnd relatedEnd, object value)229         public abstract bool CollectionRemove(RelatedEnd relatedEnd, object value);
230         public abstract object Entity { get; }
231         public abstract TEntity TypedEntity { get; }
SetCurrentValue(EntityEntry entry, StateManagerMemberMetadata member, int ordinal, object target, object value)232         public abstract void SetCurrentValue(EntityEntry entry, StateManagerMemberMetadata member, int ordinal, object target, object value);
UpdateCurrentValueRecord(object value, EntityEntry entry)233         public abstract void UpdateCurrentValueRecord(object value, EntityEntry entry);
234         public abstract bool RequiresRelationshipChangeTracking { get; }
235     }
236 }
237