//--------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- // // Internal class used to manage ObjectStateManager's transactions for // AddObject/AttachTo/DetectChanges // //--------------------------------------------------------------------- namespace System.Data.Objects.Internal { using System.Collections.Generic; using System.Data.Objects; using System.Diagnostics; using System.Data.Objects.DataClasses; class TransactionManager { #region Properties // Dictionary used to recovery after exception in ObjectContext.AttachTo() internal Dictionary> PromotedRelationships { get; private set; } // Dictionary used to recovery after exception in ObjectContext.AttachTo() internal Dictionary PromotedKeyEntries { get; private set; } // HashSet used to recover after exception in ObjectContext.Add and related methods internal HashSet PopulatedEntityReferences { get; private set; } // HashSet used to recover after exception in ObjectContext.Add and related methods internal HashSet AlignedEntityReferences { get; private set; } // Used in recovery after exception in ObjectContext.AttachTo() private MergeOption? _originalMergeOption = null; internal MergeOption? OriginalMergeOption { get { Debug.Assert(_originalMergeOption != null, "OriginalMergeOption used before being initialized"); return _originalMergeOption; } set { _originalMergeOption = value; } } // Dictionary used to recovery after exception in ObjectContext.AttachTo() and ObjectContext.AddObject() internal HashSet ProcessedEntities { get; private set; } // Used in Add/Attach/DetectChanges internal Dictionary WrappedEntities { get; private set; } // Used in Add/Attach/DetectChanges internal bool TrackProcessedEntities { get; private set; } internal bool IsAddTracking { get; private set; } internal bool IsAttachTracking { get; private set; } // Used in DetectChanges internal Dictionary>> AddedRelationshipsByGraph { get; private set; } // Used in DetectChanges internal Dictionary>> DeletedRelationshipsByGraph { get; private set; } // Used in DetectChanges internal Dictionary>> AddedRelationshipsByForeignKey { get; private set; } // Used in DetectChanges internal Dictionary>> AddedRelationshipsByPrincipalKey { get; private set; } // Used in DetectChanges internal Dictionary>> DeletedRelationshipsByForeignKey { get; private set; } // Used in DetectChanges internal Dictionary> ChangedForeignKeys { get; private set; } internal bool IsDetectChanges { get; private set; } internal bool IsAlignChanges { get; private set; } internal bool IsLocalPublicAPI { get; private set; } internal bool IsOriginalValuesGetter { get; private set; } internal bool IsForeignKeyUpdate { get; private set; } internal bool IsRelatedEndAdd { get; private set; } private int _graphUpdateCount; internal bool IsGraphUpdate { get { return _graphUpdateCount != 0; } } internal object EntityBeingReparented { get; set; } internal bool IsDetaching { get; private set; } internal EntityReference RelationshipBeingUpdated { get; private set; } internal bool IsFixupByReference { get; private set; } #endregion Properties #region Methods // Methods and properties used by recovery code in ObjectContext.AddObject() internal void BeginAddTracking() { Debug.Assert(!this.IsAddTracking); Debug.Assert(this.PopulatedEntityReferences == null, "Expected promotion index to be null when begining tracking."); Debug.Assert(this.AlignedEntityReferences == null, "Expected promotion index to be null when begining tracking."); this.IsAddTracking = true; this.PopulatedEntityReferences = new HashSet(); this.AlignedEntityReferences = new HashSet(); this.PromotedRelationships = new Dictionary>(); // BeginAddTracking can be called in the middle of DetectChanges. In this case the following flags and dictionaries should not be changed here. if (!this.IsDetectChanges) { this.TrackProcessedEntities = true; this.ProcessedEntities = new HashSet(); this.WrappedEntities = new Dictionary(); } } internal void EndAddTracking() { Debug.Assert(this.IsAddTracking); this.IsAddTracking = false; this.PopulatedEntityReferences = null; this.AlignedEntityReferences = null; this.PromotedRelationships = null; // Clear flags/dictionaries only if we are not in the iddle of DetectChanges. if (!this.IsDetectChanges) { this.TrackProcessedEntities = false; this.ProcessedEntities = null; this.WrappedEntities = null; } } // Methods and properties used by recovery code in ObjectContext.AttachTo() internal void BeginAttachTracking() { Debug.Assert(!this.IsAttachTracking); this.IsAttachTracking = true; this.PromotedRelationships = new Dictionary>(); this.PromotedKeyEntries = new Dictionary(); this.PopulatedEntityReferences = new HashSet(); this.AlignedEntityReferences = new HashSet(); this.TrackProcessedEntities = true; this.ProcessedEntities = new HashSet(); this.WrappedEntities = new Dictionary(); this.OriginalMergeOption = null; // this must be set explicitely to value!=null later when the merge option is known } internal void EndAttachTracking() { Debug.Assert(this.IsAttachTracking); this.IsAttachTracking = false; this.PromotedRelationships = null; this.PromotedKeyEntries = null; this.PopulatedEntityReferences = null; this.AlignedEntityReferences = null; this.TrackProcessedEntities = false; this.ProcessedEntities = null; this.WrappedEntities = null; this.OriginalMergeOption = null; } // This method should be called only when there is entity in OSM which doesn't implement IEntityWithRelationships internal bool BeginDetectChanges() { if (this.IsDetectChanges) { return false; } this.IsDetectChanges = true; this.TrackProcessedEntities = true; this.ProcessedEntities = new HashSet(); this.WrappedEntities = new Dictionary(); this.DeletedRelationshipsByGraph = new Dictionary>>(); this.AddedRelationshipsByGraph = new Dictionary>>(); this.DeletedRelationshipsByForeignKey = new Dictionary>>(); this.AddedRelationshipsByForeignKey = new Dictionary>>(); this.AddedRelationshipsByPrincipalKey = new Dictionary>>(); this.ChangedForeignKeys = new Dictionary>(); return true; } internal void EndDetectChanges() { Debug.Assert(this.IsDetectChanges); this.IsDetectChanges = false; this.TrackProcessedEntities = false; this.ProcessedEntities = null; this.WrappedEntities = null; this.DeletedRelationshipsByGraph = null; this.AddedRelationshipsByGraph = null; this.DeletedRelationshipsByForeignKey = null; this.AddedRelationshipsByForeignKey = null; this.AddedRelationshipsByPrincipalKey = null; this.ChangedForeignKeys = null; } internal void BeginAlignChanges() { IsAlignChanges = true; } internal void EndAlignChanges() { IsAlignChanges = false; } internal void ResetProcessedEntities() { Debug.Assert(this.ProcessedEntities != null, "ProcessedEntities should not be null"); this.ProcessedEntities.Clear(); } internal void BeginLocalPublicAPI() { Debug.Assert(!this.IsLocalPublicAPI); this.IsLocalPublicAPI = true; } internal void EndLocalPublicAPI() { Debug.Assert(this.IsLocalPublicAPI); this.IsLocalPublicAPI = false; } internal void BeginOriginalValuesGetter() { Debug.Assert(!this.IsOriginalValuesGetter); this.IsOriginalValuesGetter = true; } internal void EndOriginalValuesGetter() { Debug.Assert(this.IsOriginalValuesGetter); this.IsOriginalValuesGetter = false; } internal void BeginForeignKeyUpdate(EntityReference relationship) { Debug.Assert(!this.IsForeignKeyUpdate); this.RelationshipBeingUpdated = relationship; this.IsForeignKeyUpdate = true; } internal void EndForeignKeyUpdate() { Debug.Assert(this.IsForeignKeyUpdate); this.RelationshipBeingUpdated = null; this.IsForeignKeyUpdate = false; } internal void BeginRelatedEndAdd() { Debug.Assert(!this.IsRelatedEndAdd); this.IsRelatedEndAdd = true; } internal void EndRelatedEndAdd() { Debug.Assert(this.IsRelatedEndAdd); this.IsRelatedEndAdd = false; } internal void BeginGraphUpdate() { _graphUpdateCount++; } internal void EndGraphUpdate() { Debug.Assert(_graphUpdateCount > 0); _graphUpdateCount--; } internal void BeginDetaching() { Debug.Assert(!IsDetaching); IsDetaching = true; } internal void EndDetaching() { Debug.Assert(IsDetaching); IsDetaching = false; } internal void BeginFixupKeysByReference() { Debug.Assert(!IsFixupByReference); IsFixupByReference = true; } internal void EndFixupKeysByReference() { Debug.Assert(IsFixupByReference); IsFixupByReference = false; } #endregion Methods } }