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