//--------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner Microsoft // @backupOwner Microsoft //--------------------------------------------------------------------- using System.Collections; using System.Data.Objects.DataClasses; using System.Diagnostics; using System.Reflection; using System.Data.Metadata.Edm; namespace System.Data.Objects.Internal { /// /// Base class containing common code for different implementations of the IEntityWrapper /// interface. Generally speaking, operations involving the ObjectContext, RelationshipManager /// and raw Entity are handled through this class. /// /// The type of entity wrapped internal abstract class BaseEntityWrapper : IEntityWrapper { // This enum allows boolean flags to be added to the wrapper without introducing a new field // for each one. This helps keep the wrapper memory footprint small, which is important // in some high-performance NoTracking cases. [Flags] private enum WrapperFlags { None = 0, NoTracking = 1, InitializingRelatedEnds = 2, } private readonly RelationshipManager _relationshipManager; private Type _identityType; private WrapperFlags _flags; /// /// Constructs a wrapper for the given entity and its associated RelationshipManager. /// /// The entity to be wrapped /// the RelationshipManager associated with this entity protected BaseEntityWrapper(TEntity entity, RelationshipManager relationshipManager) { Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity."); Debug.Assert(entity != null, "Factory should ensure wrapped entity here is never null."); if (relationshipManager == null) { throw EntityUtil.UnexpectedNullRelationshipManager(); } _relationshipManager = relationshipManager; } /// /// Constructs a wrapper as part of the materialization process. This constructor is only used /// during materialization where it is known that the entity being wrapped is newly constructed. /// This means that some checks are not performed that might be needed when thw wrapper is /// created at other times, and information such as the identity type is passed in because /// it is readily available in the materializer. /// /// The entity to wrap /// The RelationshipManager associated with this entity /// The entity set, or null if none is known /// The context to which the entity should be attached /// NoTracking for non-tracked entities, AppendOnly otherwise /// The type of the entity ignoring any possible proxy type protected BaseEntityWrapper(TEntity entity, RelationshipManager relationshipManager, EntitySet entitySet, ObjectContext context, MergeOption mergeOption, Type identityType) { Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity."); Debug.Assert(entity != null, "Factory should ensure wrapped entity here is never null."); if (relationshipManager == null) { throw EntityUtil.UnexpectedNullRelationshipManager(); } _identityType = identityType; _relationshipManager = relationshipManager; RelationshipManager.SetWrappedOwner(this, entity); if (entitySet != null) { Context = context; MergeOption = mergeOption; RelationshipManager.AttachContextToRelatedEnds(context, entitySet, mergeOption); } } // See IEntityWrapper documentation public RelationshipManager RelationshipManager { get { return _relationshipManager; } } // See IEntityWrapper documentation public ObjectContext Context { get; set; } // See IEntityWrapper documentation public MergeOption MergeOption { get { return (_flags & WrapperFlags.NoTracking) != 0 ? MergeOption.NoTracking : MergeOption.AppendOnly; } private set { Debug.Assert(value == MergeOption.AppendOnly || value == MergeOption.NoTracking, "Merge option must be one of NoTracking or AppendOnly."); if (value == MergeOption.NoTracking) { _flags |= WrapperFlags.NoTracking; } else { _flags &= ~WrapperFlags.NoTracking; } } } // See IEntityWrapper documentation public bool InitializingProxyRelatedEnds { get { return (_flags & WrapperFlags.InitializingRelatedEnds) != 0; } set { if (value) { _flags |= WrapperFlags.InitializingRelatedEnds; } else { _flags &= ~WrapperFlags.InitializingRelatedEnds; } } } // See IEntityWrapper documentation public void AttachContext(ObjectContext context, EntitySet entitySet, MergeOption mergeOption) { Debug.Assert(null != context, "context"); Context = context; MergeOption = mergeOption; if (entitySet != null) { RelationshipManager.AttachContextToRelatedEnds(context, entitySet, mergeOption); } } // See IEntityWrapper documentation public void ResetContext(ObjectContext context, EntitySet entitySet, MergeOption mergeOption) { Debug.Assert(null != entitySet, "entitySet should not be null"); Debug.Assert(null != context, "context"); Debug.Assert(MergeOption.NoTracking == mergeOption || MergeOption.AppendOnly == mergeOption, "mergeOption"); if (!object.ReferenceEquals(Context, context)) { Context = context; MergeOption = mergeOption; RelationshipManager.ResetContextOnRelatedEnds(context, entitySet, mergeOption); } } // See IEntityWrapper documentation public void DetachContext() { if (Context != null && Context.ObjectStateManager.TransactionManager.IsAttachTracking && Context.ObjectStateManager.TransactionManager.OriginalMergeOption == MergeOption.NoTracking) { // If AttachTo() failed while attaching graph retrieved with NoTracking option, // we don't want to clear the Context property of the wrapped entity MergeOption = MergeOption.NoTracking; } else { Context = null; } RelationshipManager.DetachContextFromRelatedEnds(); } // See IEntityWrapper documentation public EntityEntry ObjectStateEntry { get; set; } // See IEntityWrapper documentation public Type IdentityType { get { if (_identityType == null) { _identityType = EntityUtil.GetEntityIdentityType(typeof(TEntity)); } return _identityType; } } // All these methods defined by IEntityWrapper public abstract void EnsureCollectionNotNull(RelatedEnd relatedEnd); public abstract EntityKey EntityKey { get; set; } public abstract bool OwnsRelationshipManager { get; } public abstract EntityKey GetEntityKeyFromEntity(); public abstract void SetChangeTracker(IEntityChangeTracker changeTracker); public abstract void TakeSnapshot(EntityEntry entry); public abstract void TakeSnapshotOfRelationships(EntityEntry entry); public abstract object GetNavigationPropertyValue(RelatedEnd relatedEnd); public abstract void SetNavigationPropertyValue(RelatedEnd relatedEnd, object value); public abstract void RemoveNavigationPropertyValue(RelatedEnd relatedEnd, object value); public abstract void CollectionAdd(RelatedEnd relatedEnd, object value); public abstract bool CollectionRemove(RelatedEnd relatedEnd, object value); public abstract object Entity { get; } public abstract TEntity TypedEntity { get; } public abstract void SetCurrentValue(EntityEntry entry, StateManagerMemberMetadata member, int ordinal, object target, object value); public abstract void UpdateCurrentValueRecord(object value, EntityEntry entry); public abstract bool RequiresRelationshipChangeTracking { get; } } }