1 //---------------------------------------------------------------------
2 // <copyright file="TransactionManager.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 //
10 //  Internal class used to manage ObjectStateManager's transactions for
11 //  AddObject/AttachTo/DetectChanges
12 //
13 //---------------------------------------------------------------------
14 
15 namespace System.Data.Objects.Internal
16 {
17     using System.Collections.Generic;
18     using System.Data.Objects;
19     using System.Diagnostics;
20     using System.Data.Objects.DataClasses;
21 
22     class TransactionManager
23     {
24         #region Properties
25         // Dictionary used to recovery after exception in ObjectContext.AttachTo()
26         internal Dictionary<RelatedEnd, IList<IEntityWrapper>> PromotedRelationships
27         {
28             get;
29             private set;
30         }
31 
32         // Dictionary used to recovery after exception in ObjectContext.AttachTo()
33         internal Dictionary<object, EntityEntry> PromotedKeyEntries
34         {
35             get;
36             private set;
37         }
38 
39         // HashSet used to recover after exception in ObjectContext.Add and related methods
40         internal HashSet<EntityReference> PopulatedEntityReferences
41         {
42             get;
43             private set;
44         }
45 
46         // HashSet used to recover after exception in ObjectContext.Add and related methods
47         internal HashSet<EntityReference> AlignedEntityReferences
48         {
49             get;
50             private set;
51         }
52 
53         // Used in recovery after exception in ObjectContext.AttachTo()
54         private MergeOption? _originalMergeOption = null;
55         internal MergeOption? OriginalMergeOption
56         {
57             get
58             {
59                 Debug.Assert(_originalMergeOption != null, "OriginalMergeOption used before being initialized");
60                 return _originalMergeOption;
61             }
62             set
63             {
64                 _originalMergeOption = value;
65             }
66         }
67 
68         // Dictionary used to recovery after exception in ObjectContext.AttachTo() and ObjectContext.AddObject()
69         internal HashSet<IEntityWrapper> ProcessedEntities
70         {
71             get;
72             private set;
73         }
74 
75         // Used in Add/Attach/DetectChanges
76         internal Dictionary<object, IEntityWrapper> WrappedEntities
77         {
78             get;
79             private set;
80         }
81 
82         // Used in Add/Attach/DetectChanges
83         internal bool TrackProcessedEntities
84         {
85             get;
86             private set;
87         }
88 
89         internal bool IsAddTracking
90         {
91             get;
92             private set;
93         }
94 
95         internal bool IsAttachTracking
96         {
97             get;
98             private set;
99         }
100 
101         // Used in DetectChanges
102         internal Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<IEntityWrapper>>> AddedRelationshipsByGraph
103         {
104             get;
105             private set;
106         }
107 
108         // Used in DetectChanges
109         internal Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<IEntityWrapper>>> DeletedRelationshipsByGraph
110         {
111             get;
112             private set;
113         }
114 
115         // Used in DetectChanges
116         internal Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<EntityKey>>> AddedRelationshipsByForeignKey
117         {
118             get;
119             private set;
120         }
121 
122         // Used in DetectChanges
123         internal Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<EntityKey>>> AddedRelationshipsByPrincipalKey
124         {
125             get;
126             private set;
127         }
128 
129         // Used in DetectChanges
130         internal Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<EntityKey>>> DeletedRelationshipsByForeignKey
131         {
132             get;
133             private set;
134         }
135 
136         // Used in DetectChanges
137         internal Dictionary<IEntityWrapper, HashSet<RelatedEnd>> ChangedForeignKeys
138         {
139             get;
140             private set;
141         }
142 
143         internal bool IsDetectChanges
144         {
145             get;
146             private set;
147         }
148 
149         internal bool IsAlignChanges
150         {
151             get;
152             private set;
153         }
154 
155         internal bool IsLocalPublicAPI
156         {
157             get;
158             private set;
159         }
160 
161         internal bool IsOriginalValuesGetter
162         {
163             get;
164             private set;
165         }
166 
167         internal bool IsForeignKeyUpdate
168         {
169             get;
170             private set;
171         }
172 
173         internal bool IsRelatedEndAdd
174         {
175             get;
176             private set;
177         }
178 
179         private int _graphUpdateCount;
180         internal bool IsGraphUpdate
181         {
182             get
183             {
184                 return _graphUpdateCount != 0;
185             }
186         }
187 
188         internal object EntityBeingReparented
189         {
190             get;
191             set;
192         }
193 
194         internal bool IsDetaching
195         {
196             get;
197             private set;
198         }
199 
200         internal EntityReference RelationshipBeingUpdated
201         {
202             get;
203             private set;
204         }
205 
206         internal bool IsFixupByReference
207         {
208             get;
209             private set;
210         }
211 
212         #endregion Properties
213 
214 
215         #region Methods
216 
217         // Methods and properties used by recovery code in ObjectContext.AddObject()
BeginAddTracking()218         internal void BeginAddTracking()
219         {
220             Debug.Assert(!this.IsAddTracking);
221             Debug.Assert(this.PopulatedEntityReferences == null, "Expected promotion index to be null when begining tracking.");
222             Debug.Assert(this.AlignedEntityReferences == null, "Expected promotion index to be null when begining tracking.");
223             this.IsAddTracking = true;
224             this.PopulatedEntityReferences = new HashSet<EntityReference>();
225             this.AlignedEntityReferences = new HashSet<EntityReference>();
226             this.PromotedRelationships = new Dictionary<RelatedEnd, IList<IEntityWrapper>>();
227 
228             // BeginAddTracking can be called in the middle of DetectChanges.  In this case the following flags and dictionaries should not be changed here.
229             if (!this.IsDetectChanges)
230             {
231                 this.TrackProcessedEntities = true;
232                 this.ProcessedEntities = new HashSet<IEntityWrapper>();
233                 this.WrappedEntities = new Dictionary<object, IEntityWrapper>();
234             }
235         }
236 
EndAddTracking()237         internal void EndAddTracking()
238         {
239             Debug.Assert(this.IsAddTracking);
240             this.IsAddTracking = false;
241             this.PopulatedEntityReferences = null;
242             this.AlignedEntityReferences = null;
243             this.PromotedRelationships = null;
244 
245             // Clear flags/dictionaries only if we are not in the iddle of DetectChanges.
246             if (!this.IsDetectChanges)
247             {
248                 this.TrackProcessedEntities = false;
249 
250                 this.ProcessedEntities = null;
251                 this.WrappedEntities = null;
252             }
253         }
254 
255         // Methods and properties used by recovery code in ObjectContext.AttachTo()
BeginAttachTracking()256         internal void BeginAttachTracking()
257         {
258             Debug.Assert(!this.IsAttachTracking);
259             this.IsAttachTracking = true;
260 
261             this.PromotedRelationships = new Dictionary<RelatedEnd, IList<IEntityWrapper>>();
262             this.PromotedKeyEntries = new Dictionary<object, EntityEntry>();
263             this.PopulatedEntityReferences = new HashSet<EntityReference>();
264             this.AlignedEntityReferences = new HashSet<EntityReference>();
265 
266             this.TrackProcessedEntities = true;
267             this.ProcessedEntities = new HashSet<IEntityWrapper>();
268             this.WrappedEntities = new Dictionary<object, IEntityWrapper>();
269 
270             this.OriginalMergeOption = null;  // this must be set explicitely to value!=null later when the merge option is known
271         }
272 
EndAttachTracking()273         internal void EndAttachTracking()
274         {
275             Debug.Assert(this.IsAttachTracking);
276             this.IsAttachTracking = false;
277 
278             this.PromotedRelationships = null;
279             this.PromotedKeyEntries = null;
280             this.PopulatedEntityReferences = null;
281             this.AlignedEntityReferences = null;
282 
283             this.TrackProcessedEntities = false;
284 
285             this.ProcessedEntities = null;
286             this.WrappedEntities = null;
287 
288             this.OriginalMergeOption = null;
289         }
290 
291         // This method should be called only when there is entity in OSM which doesn't implement IEntityWithRelationships
BeginDetectChanges()292         internal bool BeginDetectChanges()
293         {
294             if (this.IsDetectChanges)
295             {
296                 return false;
297             }
298             this.IsDetectChanges = true;
299 
300             this.TrackProcessedEntities = true;
301 
302             this.ProcessedEntities = new HashSet<IEntityWrapper>();
303             this.WrappedEntities = new Dictionary<object, IEntityWrapper>();
304 
305             this.DeletedRelationshipsByGraph = new Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<IEntityWrapper>>>();
306             this.AddedRelationshipsByGraph = new Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<IEntityWrapper>>>();
307             this.DeletedRelationshipsByForeignKey = new Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<EntityKey>>>();
308             this.AddedRelationshipsByForeignKey = new Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<EntityKey>>>();
309             this.AddedRelationshipsByPrincipalKey = new Dictionary<IEntityWrapper, Dictionary<RelatedEnd, HashSet<EntityKey>>>();
310             this.ChangedForeignKeys = new Dictionary<IEntityWrapper, HashSet<RelatedEnd>>();
311             return true;
312         }
313 
EndDetectChanges()314         internal void EndDetectChanges()
315         {
316             Debug.Assert(this.IsDetectChanges);
317             this.IsDetectChanges = false;
318 
319             this.TrackProcessedEntities = false;
320 
321             this.ProcessedEntities = null;
322             this.WrappedEntities = null;
323 
324             this.DeletedRelationshipsByGraph = null;
325             this.AddedRelationshipsByGraph = null;
326             this.DeletedRelationshipsByForeignKey = null;
327             this.AddedRelationshipsByForeignKey = null;
328             this.AddedRelationshipsByPrincipalKey = null;
329             this.ChangedForeignKeys = null;
330         }
331 
BeginAlignChanges()332         internal void BeginAlignChanges()
333         {
334             IsAlignChanges = true;
335         }
336 
EndAlignChanges()337         internal void EndAlignChanges()
338         {
339             IsAlignChanges = false;
340         }
341 
ResetProcessedEntities()342         internal void ResetProcessedEntities()
343         {
344             Debug.Assert(this.ProcessedEntities != null, "ProcessedEntities should not be null");
345             this.ProcessedEntities.Clear();
346         }
347 
BeginLocalPublicAPI()348         internal void BeginLocalPublicAPI()
349         {
350             Debug.Assert(!this.IsLocalPublicAPI);
351 
352             this.IsLocalPublicAPI = true;
353         }
354 
EndLocalPublicAPI()355         internal void EndLocalPublicAPI()
356         {
357             Debug.Assert(this.IsLocalPublicAPI);
358 
359             this.IsLocalPublicAPI = false;
360         }
361 
BeginOriginalValuesGetter()362         internal void BeginOriginalValuesGetter()
363         {
364             Debug.Assert(!this.IsOriginalValuesGetter);
365 
366             this.IsOriginalValuesGetter = true;
367         }
368 
EndOriginalValuesGetter()369         internal void EndOriginalValuesGetter()
370         {
371             Debug.Assert(this.IsOriginalValuesGetter);
372 
373             this.IsOriginalValuesGetter = false;
374         }
375 
BeginForeignKeyUpdate(EntityReference relationship)376         internal void BeginForeignKeyUpdate(EntityReference relationship)
377         {
378             Debug.Assert(!this.IsForeignKeyUpdate);
379 
380             this.RelationshipBeingUpdated = relationship;
381             this.IsForeignKeyUpdate = true;
382         }
383 
EndForeignKeyUpdate()384         internal void EndForeignKeyUpdate()
385         {
386             Debug.Assert(this.IsForeignKeyUpdate);
387 
388             this.RelationshipBeingUpdated = null;
389             this.IsForeignKeyUpdate = false;
390         }
391 
BeginRelatedEndAdd()392         internal void BeginRelatedEndAdd()
393         {
394             Debug.Assert(!this.IsRelatedEndAdd);
395             this.IsRelatedEndAdd = true;
396         }
397 
EndRelatedEndAdd()398         internal void EndRelatedEndAdd()
399         {
400             Debug.Assert(this.IsRelatedEndAdd);
401             this.IsRelatedEndAdd = false;
402         }
403 
BeginGraphUpdate()404         internal void BeginGraphUpdate()
405         {
406             _graphUpdateCount++;
407         }
408 
EndGraphUpdate()409         internal void EndGraphUpdate()
410         {
411             Debug.Assert(_graphUpdateCount > 0);
412             _graphUpdateCount--;
413         }
414 
BeginDetaching()415         internal void BeginDetaching()
416         {
417             Debug.Assert(!IsDetaching);
418             IsDetaching = true;
419         }
420 
EndDetaching()421         internal void EndDetaching()
422         {
423             Debug.Assert(IsDetaching);
424             IsDetaching = false;
425         }
426 
BeginFixupKeysByReference()427         internal void BeginFixupKeysByReference()
428         {
429             Debug.Assert(!IsFixupByReference);
430             IsFixupByReference = true;
431         }
432 
EndFixupKeysByReference()433         internal void EndFixupKeysByReference()
434         {
435             Debug.Assert(IsFixupByReference);
436             IsFixupByReference = false;
437         }
438 
439         #endregion Methods
440 
441     }
442 }
443