443 lines
13 KiB
C#
443 lines
13 KiB
C#
|
//---------------------------------------------------------------------
|
|||
|
// <copyright file="TransactionManager.cs" company="Microsoft">
|
|||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|||
|
// </copyright>
|
|||
|
//
|
|||
|
// @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<RelatedEnd, IList<IEntityWrapper>> PromotedRelationships
|
|||
|
{
|
|||
|
get;
|
|||
|
private set;
|
|||
|
}
|
|||
|
|
|||
|
// Dictionary used to recovery after exception in ObjectContext.AttachTo()
|
|||
|
internal Dictionary<object, EntityEntry> PromotedKeyEntries
|
|||
|
{
|
|||
|
get;
|
|||
|
private set;
|
|||
|
}
|
|||
|
|
|||
|
// HashSet used to recover after exception in ObjectContext.Add and related methods
|
|||
|
internal HashSet<EntityReference> PopulatedEntityReferences
|
|||
|
{
|
|||
|
get;
|
|||
|
private set;
|
|||
|
}
|
|||
|
|
|||
|
// HashSet used to recover after exception in ObjectContext.Add and related methods
|
|||
|
internal HashSet<EntityReference> 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<IEntityWrapper> ProcessedEntities
|
|||
|
{
|
|||
|
get;
|
|||
|
private set;
|
|||
|
}
|
|||
|
|
|||
|
// Used in Add/Attach/DetectChanges
|
|||
|
internal Dictionary<object, IEntityWrapper> 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<IEntityWrapper, Dictionary<RelatedEnd, HashSet<IEntityWrapper>>> AddedRelationshipsByGraph
|
|||
|
{
|
|||
|
get;
|
|||
|
private set;
|
|||
|
}
|
|||
|
|
|||
|
// Used in DetectChanges
|
|||
|
internal Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<IEntityWrapper>>> DeletedRelationshipsByGraph
|
|||
|
{
|
|||
|
get;
|
|||
|
private set;
|
|||
|
}
|
|||
|
|
|||
|
// Used in DetectChanges
|
|||
|
internal Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<EntityKey>>> AddedRelationshipsByForeignKey
|
|||
|
{
|
|||
|
get;
|
|||
|
private set;
|
|||
|
}
|
|||
|
|
|||
|
// Used in DetectChanges
|
|||
|
internal Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<EntityKey>>> AddedRelationshipsByPrincipalKey
|
|||
|
{
|
|||
|
get;
|
|||
|
private set;
|
|||
|
}
|
|||
|
|
|||
|
// Used in DetectChanges
|
|||
|
internal Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<EntityKey>>> DeletedRelationshipsByForeignKey
|
|||
|
{
|
|||
|
get;
|
|||
|
private set;
|
|||
|
}
|
|||
|
|
|||
|
// Used in DetectChanges
|
|||
|
internal Dictionary<IEntityWrapper, HashSet<RelatedEnd>> 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<EntityReference>();
|
|||
|
this.AlignedEntityReferences = new HashSet<EntityReference>();
|
|||
|
this.PromotedRelationships = new Dictionary<RelatedEnd, IList<IEntityWrapper>>();
|
|||
|
|
|||
|
// 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<IEntityWrapper>();
|
|||
|
this.WrappedEntities = new Dictionary<object, IEntityWrapper>();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
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<RelatedEnd, IList<IEntityWrapper>>();
|
|||
|
this.PromotedKeyEntries = new Dictionary<object, EntityEntry>();
|
|||
|
this.PopulatedEntityReferences = new HashSet<EntityReference>();
|
|||
|
this.AlignedEntityReferences = new HashSet<EntityReference>();
|
|||
|
|
|||
|
this.TrackProcessedEntities = true;
|
|||
|
this.ProcessedEntities = new HashSet<IEntityWrapper>();
|
|||
|
this.WrappedEntities = new Dictionary<object, IEntityWrapper>();
|
|||
|
|
|||
|
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<IEntityWrapper>();
|
|||
|
this.WrappedEntities = new Dictionary<object, IEntityWrapper>();
|
|||
|
|
|||
|
this.DeletedRelationshipsByGraph = new Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<IEntityWrapper>>>();
|
|||
|
this.AddedRelationshipsByGraph = new Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<IEntityWrapper>>>();
|
|||
|
this.DeletedRelationshipsByForeignKey = new Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<EntityKey>>>();
|
|||
|
this.AddedRelationshipsByForeignKey = new Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<EntityKey>>>();
|
|||
|
this.AddedRelationshipsByPrincipalKey = new Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<EntityKey>>>();
|
|||
|
this.ChangedForeignKeys = new Dictionary<IEntityWrapper, HashSet<RelatedEnd>>();
|
|||
|
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
|
|||
|
|
|||
|
}
|
|||
|
}
|