1 //---------------------------------------------------------------------
2 // <copyright file="BaseEntityWrapper.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
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;
15 namespace System.Data.Objects.Internal
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.
22 /// <typeparam name="TEntity">The type of entity wrapped</typeparam>
23 internal abstract class BaseEntityWrapper<TEntity> : IEntityWrapper
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.
29 private enum WrapperFlags
33 InitializingRelatedEnds = 2,
36 private readonly RelationshipManager _relationshipManager;
37 private Type _identityType;
38 private WrapperFlags _flags;
41 /// Constructs a wrapper for the given entity and its associated RelationshipManager.
43 /// <param name="entity">The entity to be wrapped</param>
44 /// <param name="relationshipManager">the RelationshipManager associated with this entity</param>
45 protected BaseEntityWrapper(TEntity entity, RelationshipManager relationshipManager)
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)
51 throw EntityUtil.UnexpectedNullRelationshipManager();
53 _relationshipManager = relationshipManager;
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.
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>
69 protected BaseEntityWrapper(TEntity entity, RelationshipManager relationshipManager, EntitySet entitySet, ObjectContext context, MergeOption mergeOption, Type identityType)
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)
75 throw EntityUtil.UnexpectedNullRelationshipManager();
77 _identityType = identityType;
78 _relationshipManager = relationshipManager;
79 RelationshipManager.SetWrappedOwner(this, entity);
80 if (entitySet != null)
83 MergeOption = mergeOption;
84 RelationshipManager.AttachContextToRelatedEnds(context, entitySet, mergeOption);
89 // See IEntityWrapper documentation
90 public RelationshipManager RelationshipManager
94 return _relationshipManager;
98 // See IEntityWrapper documentation
99 public ObjectContext Context
105 // See IEntityWrapper documentation
106 public MergeOption MergeOption
110 return (_flags & WrapperFlags.NoTracking) != 0 ? MergeOption.NoTracking : MergeOption.AppendOnly;
114 Debug.Assert(value == MergeOption.AppendOnly || value == MergeOption.NoTracking, "Merge option must be one of NoTracking or AppendOnly.");
115 if (value == MergeOption.NoTracking)
117 _flags |= WrapperFlags.NoTracking;
121 _flags &= ~WrapperFlags.NoTracking;
126 // See IEntityWrapper documentation
127 public bool InitializingProxyRelatedEnds
131 return (_flags & WrapperFlags.InitializingRelatedEnds) != 0;
137 _flags |= WrapperFlags.InitializingRelatedEnds;
141 _flags &= ~WrapperFlags.InitializingRelatedEnds;
146 // See IEntityWrapper documentation
147 public void AttachContext(ObjectContext context, EntitySet entitySet, MergeOption mergeOption)
149 Debug.Assert(null != context, "context");
151 MergeOption = mergeOption;
152 if (entitySet != null)
154 RelationshipManager.AttachContextToRelatedEnds(context, entitySet, mergeOption);
158 // See IEntityWrapper documentation
159 public void ResetContext(ObjectContext context, EntitySet entitySet, MergeOption mergeOption)
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,
167 if (!object.ReferenceEquals(Context, context))
170 MergeOption = mergeOption;
171 RelationshipManager.ResetContextOnRelatedEnds(context, entitySet, mergeOption);
175 // See IEntityWrapper documentation
176 public void DetachContext()
178 if (Context != null &&
179 Context.ObjectStateManager.TransactionManager.IsAttachTracking &&
180 Context.ObjectStateManager.TransactionManager.OriginalMergeOption == MergeOption.NoTracking)
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;
191 RelationshipManager.DetachContextFromRelatedEnds();
194 // See IEntityWrapper documentation
195 public EntityEntry ObjectStateEntry
201 // See IEntityWrapper documentation
202 public Type IdentityType
206 if (_identityType == null)
208 _identityType = EntityUtil.GetEntityIdentityType(typeof(TEntity));
210 return _identityType;
214 // All these methods defined by IEntityWrapper
215 public abstract void EnsureCollectionNotNull(RelatedEnd relatedEnd);
216 public abstract EntityKey EntityKey { get; set; }
217 public abstract bool OwnsRelationshipManager
221 public abstract EntityKey GetEntityKeyFromEntity();
222 public abstract void SetChangeTracker(IEntityChangeTracker changeTracker);
223 public abstract void TakeSnapshot(EntityEntry entry);
224 public abstract void TakeSnapshotOfRelationships(EntityEntry entry);
225 public abstract object GetNavigationPropertyValue(RelatedEnd relatedEnd);
226 public abstract void SetNavigationPropertyValue(RelatedEnd relatedEnd, object value);
227 public abstract void RemoveNavigationPropertyValue(RelatedEnd relatedEnd, object value);
228 public abstract void CollectionAdd(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; }
232 public abstract void SetCurrentValue(EntityEntry entry, StateManagerMemberMetadata member, int ordinal, object target, object value);
233 public abstract void UpdateCurrentValueRecord(object value, EntityEntry entry);
234 public abstract bool RequiresRelationshipChangeTracking { get; }