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