Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Objects / ObjectStateManager.cs
1 //---------------------------------------------------------------------
2 // <copyright file="ObjectStateManager.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 namespace System.Data.Objects
11 {
12     using System.Collections;
13     using System.Collections.Generic;
14     using System.ComponentModel;
15     using System.Data.Common;
16     using System.Data.Common.Utils;
17     using System.Data.Mapping;
18     using System.Data.Metadata.Edm;
19     using System.Data.Objects.DataClasses;
20     using System.Data.Objects.Internal;
21     using System.Diagnostics;
22     using System.Globalization;
23     using System.Linq;
24     using System.Linq.Expressions;
25
26     /// <summary>
27     /// implementation of ObjectStateManager class
28     /// </summary>
29     public class ObjectStateManager : IEntityStateManager
30     {
31         // This is the initial capacity used for lists of entries.  We use this rather than the default because
32         // perf testing showed we were almost always increasing the capacity which can be quite a slow operation.
33         private const int _initialListSize = 16;
34
35         // dictionaries (one for each entity state) that store cache entries that represent entities
36         // these are only non-null when there is an entity in respective state, must always check for null before using
37         private Dictionary<EntityKey, EntityEntry> _addedEntityStore;
38         private Dictionary<EntityKey, EntityEntry> _modifiedEntityStore;
39         private Dictionary<EntityKey, EntityEntry> _deletedEntityStore;
40         private Dictionary<EntityKey, EntityEntry> _unchangedEntityStore;
41         private Dictionary<object, EntityEntry> _keylessEntityStore;
42
43         // dictionaries (one for each entity state) that store cache entries that represent relationships
44         // these are only non-null when there is an relationship in respective state, must always check for null before using
45         private Dictionary<RelationshipWrapper, RelationshipEntry> _addedRelationshipStore;
46         private Dictionary<RelationshipWrapper, RelationshipEntry> _deletedRelationshipStore;
47         private Dictionary<RelationshipWrapper, RelationshipEntry> _unchangedRelationshipStore;
48
49         // mapping from EdmType or EntitySetQualifiedType to StateManagerTypeMetadata
50         private readonly Dictionary<EdmType, StateManagerTypeMetadata> _metadataStore;
51         private readonly Dictionary<EntitySetQualifiedType, StateManagerTypeMetadata> _metadataMapping;
52
53         private readonly MetadataWorkspace _metadataWorkspace;
54
55         // delegate for notifying changes in collection
56         private CollectionChangeEventHandler onObjectStateManagerChangedDelegate;
57         private CollectionChangeEventHandler onEntityDeletedDelegate;
58
59         // Flag to indicate if we are in the middle of relationship fixup.
60         // This is set and cleared only during ResetEntityKey, because only in that case
61         // do we allow setting a value on a non-null EntityKey property
62         private bool _inRelationshipFixup;
63
64         private bool _isDisposed;
65
66         private ComplexTypeMaterializer _complexTypeMaterializer; // materializer instance that can be used to create complex types with just a metadata workspace
67
68         private readonly Dictionary<EntityKey, HashSet<EntityEntry>> _danglingForeignKeys = new Dictionary<EntityKey, HashSet<EntityEntry>>();
69         private HashSet<EntityEntry> _entriesWithConceptualNulls;
70
71         #region Private Fields for ObjectStateEntry change tracking
72
73         /// <summary>
74         /// The object on which the change was made, could be an entity or a complex object
75         /// Also see comments for _changingOldValue
76         /// </summary>
77         private object _changingObject;
78
79         /// <summary>
80         /// _changingMember and _changingEntityMember should be the same for changes to
81         /// top-level entity properties. They are only different for complex members.
82         /// Also see comments for _changingOldValue.
83         /// </summary>
84         private string _changingMember;
85
86         /// <summary>
87         /// The top-level entity property that is changing. This could be the actual property
88         /// that is changing, or the change could be occurring one or more levels down in the hierarchy
89         /// on a complex object.
90         /// Also see comments for _changingOldValue
91         /// </summary>
92         private string _changingEntityMember;
93
94         /// <summary>
95         /// EntityState of the entry during the changing notification. Used to detect
96         /// if the state has changed between the changing and changed notifications, which we do not allow.
97         /// Also see comments for _changingOldValue
98         /// </summary>
99         private EntityState _changingState;
100
101         /// <summary>
102         /// True if we are in a state where original values are to be stored during a changed notification        
103         /// This flags gets set during the changing notification and is used during changed to determine
104         /// if we need to save to original values or not.
105         /// Also see comments for _changingOldValue
106         private bool _saveOriginalValues;
107
108         /// <summary>
109         /// Cache entity property/changing object/original value here between changing and changed events
110         /// </summary>
111         /// <remarks>
112         /// Only single threading is supported and changing/changed calls cannot be nested. Consecutive calls to Changing
113         /// overwrite previously cached values. Calls to Changed must have been preceded by a Changing call on the same property
114         ///
115         /// a) user starts property value change with a call to
116         ///    IEntityChangeTracker.EntityMemberChanging or IEntityChangeTracker.EntityComplexMemberChanging
117         /// b) The public interface methods call EntityValueChanging, which caches the original value
118         /// c) new property value is stored on object
119         /// d) users completes the property value change with a call to
120         ///    IEntityChangeTracker.EntityMemberChanged or IEntityChangeTracker.EntityComplexMemberChanged
121         /// e} The public interface methods call EntityValueChanged, which saves the cached value in the original values record
122         /// </remarks>
123         private object _changingOldValue;
124
125         private bool _detectChangesNeeded;
126
127         #endregion
128
129         /// <summary>
130         /// ObjectStateManager constructor.
131         /// </summary>
132         /// <param name="metadataWorkspace"></param>
133         [CLSCompliant(false)]
134         public ObjectStateManager(MetadataWorkspace metadataWorkspace)
135         {
136             EntityUtil.CheckArgumentNull(metadataWorkspace, "metadataWorkspace");
137             _metadataWorkspace = metadataWorkspace;
138
139             _metadataStore = new Dictionary<EdmType, StateManagerTypeMetadata>();
140             _metadataMapping = new Dictionary<EntitySetQualifiedType, StateManagerTypeMetadata>(EntitySetQualifiedType.EqualityComparer);
141             _isDisposed = false;
142             TransactionManager = new TransactionManager();
143         }
144
145         #region Internal Properties for ObjectStateEntry change tracking
146
147         internal object ChangingObject
148         {
149             get { return _changingObject; }
150             set { _changingObject = value; }
151         }
152
153         internal string ChangingEntityMember
154         {
155             get { return _changingEntityMember; }
156             set { _changingEntityMember = value; }
157         }
158
159         internal string ChangingMember
160         {
161             get { return _changingMember; }
162             set { _changingMember = value; }
163         }
164
165         internal EntityState ChangingState
166         {
167             get { return _changingState; }
168             set { _changingState = value; }
169         }
170
171         internal bool SaveOriginalValues
172         {
173             get { return _saveOriginalValues; }
174             set { _saveOriginalValues = value; }
175         }
176
177         internal object ChangingOldValue
178         {
179             get { return _changingOldValue; }
180             set { _changingOldValue = value; }
181         }
182
183         // Used by ObjectStateEntry to determine if it's safe to set a value
184         // on a non-null IEntity.EntityKey property
185         internal bool InRelationshipFixup
186         {
187             get { return _inRelationshipFixup; }
188         }
189
190         internal ComplexTypeMaterializer ComplexTypeMaterializer
191         {
192             get
193             {
194                 if (_complexTypeMaterializer == null)
195                 {
196                     _complexTypeMaterializer = new ComplexTypeMaterializer(this.MetadataWorkspace);
197                 }
198                 return _complexTypeMaterializer;
199             }
200         }
201
202         #endregion
203
204         internal TransactionManager TransactionManager
205         {
206             get;
207             private set;
208         }
209
210         /// <summary>
211         /// MetadataWorkspace property
212         /// </summary>
213         /// <returns>MetadataWorkspace</returns>
214         [CLSCompliant(false)]
215         public MetadataWorkspace MetadataWorkspace
216         {
217             get
218             {
219                 return _metadataWorkspace;
220             }
221         }
222
223         #region events ObjectStateManagerChanged / EntityDeleted
224         /// <summary>
225         /// Event to notify changes in the collection.
226         /// </summary>
227         public event CollectionChangeEventHandler ObjectStateManagerChanged
228         {
229             add
230             {
231                 onObjectStateManagerChangedDelegate += value;
232             }
233             remove
234             {
235                 onObjectStateManagerChangedDelegate -= value;
236             }
237         }
238
239         internal event CollectionChangeEventHandler EntityDeleted
240         {
241             add
242             {
243                 onEntityDeletedDelegate += value;
244             }
245             remove
246             {
247                 onEntityDeletedDelegate -= value;
248             }
249         }
250
251         internal void OnObjectStateManagerChanged(CollectionChangeAction action, object entity)
252         {
253             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
254             if (onObjectStateManagerChangedDelegate != null)
255             {
256                 onObjectStateManagerChangedDelegate(this, new CollectionChangeEventArgs(action, entity));
257             }
258         }
259
260         private void OnEntityDeleted(CollectionChangeAction action, object entity)
261         {
262             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
263             if (onEntityDeletedDelegate != null)
264             {
265                 onEntityDeletedDelegate(this, new CollectionChangeEventArgs(action, entity));
266             }
267         }
268         #endregion
269
270         /// <summary>
271         /// Adds an object stub to the cache.
272         /// </summary>
273         /// <param name="entityKey">the key of the object to add</param>
274         /// <param name="entitySet">the entity set of the given object</param>
275         /// 
276         internal EntityEntry AddKeyEntry(EntityKey entityKey, EntitySet entitySet)
277         {
278             Debug.Assert((object)entityKey != null, "entityKey cannot be null.");
279             Debug.Assert(entitySet != null, "entitySet must be non-null.");
280
281             // We need to determine if an equivalent entry already exists;
282             // this is illegal in certain cases.
283             EntityEntry entry = FindEntityEntry(entityKey);
284             if (entry != null)
285             {
286                 throw EntityUtil.ObjectStateManagerContainsThisEntityKey();
287             }
288
289             // Get a StateManagerTypeMetadata for the entity type.
290             StateManagerTypeMetadata typeMetadata = GetOrAddStateManagerTypeMetadata(entitySet.ElementType);
291
292             // Create a cache entry.
293             entry = new EntityEntry(entityKey, entitySet, this, typeMetadata);
294
295             // A new entity is being added.
296             AddEntityEntryToDictionary(entry, entry.State);
297
298             return entry;
299         }
300
301         /// <summary>
302         /// Validates that the proxy type being attached to the context matches the proxy type
303         /// that would be generated for the given CLR type for the currently loaded metadata.
304         /// This prevents a proxy for one set of metadata being incorrectly loaded into a context
305         /// which has different metadata.
306         /// </summary>
307         private void ValidateProxyType(IEntityWrapper wrappedEntity)
308         {
309             var identityType = wrappedEntity.IdentityType;
310             var actualType = wrappedEntity.Entity.GetType();
311             if (identityType != actualType)
312             {
313                 var entityType = MetadataWorkspace.GetItem<ClrEntityType>(identityType.FullName, DataSpace.OSpace);
314                 var proxyTypeInfo = EntityProxyFactory.GetProxyType(entityType);
315                 if (proxyTypeInfo == null || proxyTypeInfo.ProxyType != actualType)
316                 {
317                     throw EntityUtil.DuplicateTypeForProxyType(identityType);
318                 }
319             }
320         }
321
322         /// <summary>
323         /// Adds an object to the ObjectStateManager.
324         /// </summary>
325         /// <param name="dataObject">the object to add</param>
326         /// <param name="entitySet">the entity set of the given object</param>
327         /// <param name="argumentName">Name of the argument passed to a public method, for use in exceptions.</param>
328         /// <param name="isAdded">Indicates whether the entity is added or unchanged.</param>
329         internal EntityEntry AddEntry(IEntityWrapper wrappedObject, EntityKey passedKey, EntitySet entitySet, string argumentName, bool isAdded)
330         {
331             Debug.Assert(wrappedObject != null, "entity wrapper cannot be null.");
332             Debug.Assert(wrappedObject.Entity != null, "entity cannot be null.");
333             Debug.Assert(wrappedObject.Context != null, "the context should be already set");
334             Debug.Assert(entitySet != null, "entitySet must be non-null.");
335             // shadowValues is allowed to be null
336             Debug.Assert(argumentName != null, "argumentName cannot be null.");
337
338             EntityKey entityKey = passedKey;
339
340             // Get a StateManagerTypeMetadata for the entity type.
341             StateManagerTypeMetadata typeMetadata = GetOrAddStateManagerTypeMetadata(wrappedObject.IdentityType, entitySet);
342
343             ValidateProxyType(wrappedObject);
344
345             // dataObject's type should match to type that can be contained by the entityset
346             EdmType entityEdmType = typeMetadata.CdmMetadata.EdmType;
347             //OC Mapping will make sure that non-abstract type in O space is always mapped to a non-abstract type in C space
348             Debug.Assert(!entityEdmType.Abstract, "non-abstract type in O space is mapped to abstract type in C space");
349             if ((isAdded) && !entitySet.ElementType.IsAssignableFrom(entityEdmType))
350             {
351                 throw EntityUtil.EntityTypeDoesNotMatchEntitySet(wrappedObject.Entity.GetType().Name, TypeHelpers.GetFullName(entitySet), argumentName);
352             }
353
354             EntityKey dataObjectEntityKey = null;
355             if (isAdded)
356             {
357                 dataObjectEntityKey = wrappedObject.GetEntityKeyFromEntity();
358             }
359             else
360             {
361                 dataObjectEntityKey = wrappedObject.EntityKey;
362             }
363 #if DEBUG
364             if ((object)dataObjectEntityKey != null && (object)entityKey != null)
365             {
366                 Debug.Assert(dataObjectEntityKey == entityKey, "The passed key and the key on dataObject must match.");
367             }
368 #endif
369             if (null != (object)dataObjectEntityKey)
370             {
371                 entityKey = dataObjectEntityKey;
372                 // These two checks verify that entityWithKey.EntityKey implemented by the user on a (I)POCO entity returns what it was given.
373                 EntityUtil.CheckEntityKeyNull(entityKey);
374                 EntityUtil.CheckEntityKeysMatch(wrappedObject, entityKey);
375             }
376
377             if ((object)entityKey != null && !entityKey.IsTemporary && !isAdded)
378             {
379                 // If the entity already has a permanent key, and we were invoked
380                 // from the materializer, check that the key is correct.  We don't check
381                 // for temporary keys because temporary keys don't contain values.
382                 CheckKeyMatchesEntity(wrappedObject, entityKey, entitySet, /*forAttach*/ false);
383             }
384
385             // We need to determine if an equivalent entry already exists; this is illegal
386             // in certain cases.
387             EntityEntry existingEntry;
388             if ((isAdded) &&
389                 ((dataObjectEntityKey == null && (null != (existingEntry = FindEntityEntry(wrappedObject.Entity)))) ||
390                  (dataObjectEntityKey != null && (null != (existingEntry = FindEntityEntry(dataObjectEntityKey))))))
391             {
392                 if (existingEntry.Entity != wrappedObject.Entity)
393                 {
394                     throw EntityUtil.ObjectStateManagerContainsThisEntityKey();
395                 }
396                 // key does exist but entity is the same, it is being re-added ;
397                 // no-op when Add(entity)
398                 // NOTE we don't want to re-add entities in other then Added state
399                 if (existingEntry.State != EntityState.Added)  // (state == DataRowState.Unchanged && state == DataRowState.Modified)
400                 {
401                     throw EntityUtil.ObjectStateManagerDoesnotAllowToReAddUnchangedOrModifiedOrDeletedEntity(existingEntry.State);
402                 }
403
404                 // no-op
405                 return null;
406             }
407             else
408             {
409                 // Neither entityWithKey.EntityKey nor the passed entityKey were non-null, or
410                 // If the entity doesn't already exist in the state manager
411                 // and we intend to put the entity in the Added state (i.e.,
412                 // AddEntry() was invoked from ObjectContext.AddObject()),
413                 // the entity's key must be set to a new temp key.
414                 if ((object)entityKey == null || (isAdded && !entityKey.IsTemporary))
415                 {
416                     // If the entity does not have a key, create and add a temporary key.
417                     entityKey = new EntityKey(entitySet);
418                     wrappedObject.EntityKey = entityKey;
419                 }
420
421                 if (!wrappedObject.OwnsRelationshipManager)
422                 {
423                     // When a POCO instance is added or attached, we need to ignore the contents 
424                     // of the RelationshipManager as it is out-of-date with the POCO nav props
425                     wrappedObject.RelationshipManager.ClearRelatedEndWrappers();
426                 }
427
428                 // Create a cache entry.
429                 EntityEntry newEntry = new EntityEntry(wrappedObject, entityKey, entitySet, this, typeMetadata, isAdded ? EntityState.Added : EntityState.Unchanged);
430
431                 //Verify that the entityKey is set correctly--also checks entry.EK and entity.EK internally
432                 Debug.Assert(entityKey == newEntry.EntityKey, "The key on the new entry was not set correctly");
433
434                 // ObjectMaterializer will have already determined the existing entry doesn't exist
435                 Debug.Assert(null == FindEntityEntry(entityKey), "should not have existing entry");
436
437                 // A new entity is being added.
438                 newEntry.AttachObjectStateManagerToEntity();
439                 AddEntityEntryToDictionary(newEntry, newEntry.State);
440
441                 // fire ColectionChanged event  only when a new entity is added to cache
442                 OnObjectStateManagerChanged(CollectionChangeAction.Add, newEntry.Entity);
443
444                 // When adding, we do this in AddSingleObject since we don't want to do it before the context is attached.
445                 if (!isAdded)
446                 {
447                     FixupReferencesByForeignKeys(newEntry);
448                 }
449
450                 return newEntry;
451             }
452         }
453
454         internal void FixupReferencesByForeignKeys(EntityEntry newEntry, bool replaceAddedRefs = false)
455         {
456             // Perf optimization to avoid all this work if the entity doesn't participate in any FK relationships
457             if (!((EntitySet)newEntry.EntitySet).HasForeignKeyRelationships)
458             {
459                 return;
460             }
461
462             // Look at the foreign keys contained in this entry and perform fixup to the entities that
463             // they reference, or add the key and this entry to the index of foreign keys that reference
464             // entities that we don't yet know about.
465             newEntry.FixupReferencesByForeignKeys(replaceAddedRefs);
466             // Lookup the key for this entry and find all other entries that reference this entry using
467             // foreign keys.  Perform fixup between the two entries.
468             foreach (EntityEntry foundEntry in GetNonFixedupEntriesContainingForeignKey(newEntry.EntityKey))
469             {
470                 foundEntry.FixupReferencesByForeignKeys(replaceAddedRefs: false);
471             }
472             // Once we have done fixup for this entry we don't need the entries in the index anymore
473             RemoveForeignKeyFromIndex(newEntry.EntityKey);
474         }
475
476         /// <summary>
477         /// Adds an entry to the index of foreign keys that reference entities that we don't yet know about.
478         /// </summary>
479         /// <param name="foreignKey">The foreign key found in the entry</param>
480         /// <param name="entry">The entry that contains the foreign key that was found</param>
481         internal void AddEntryContainingForeignKeyToIndex(EntityKey foreignKey, EntityEntry entry)
482         {
483             HashSet<EntityEntry> danglingEntries;
484             if (!_danglingForeignKeys.TryGetValue(foreignKey, out danglingEntries))
485             {
486                 danglingEntries = new HashSet<EntityEntry>();
487                 _danglingForeignKeys.Add(foreignKey, danglingEntries);
488             }
489             Debug.Assert(entry.ObjectStateManager != null, "Attempt to add detached state entry to dangling keys");
490             danglingEntries.Add(entry);
491         }
492
493         [ConditionalAttribute("DEBUG")]
494         internal void AssertEntryDoesNotExistInForeignKeyIndex(EntityEntry entry)
495         {
496             foreach (var dFkEntry in _danglingForeignKeys.SelectMany(kv => kv.Value))
497             {
498                 if (!(dFkEntry.State == EntityState.Detached || entry.State == EntityState.Detached))
499                 {
500                     Debug.Assert(dFkEntry.EntityKey == null || entry.EntityKey == null ||
501                                  (dFkEntry.EntityKey != entry.EntityKey && dFkEntry != entry),
502                         string.Format(CultureInfo.InvariantCulture, "The entry references {0} equal. dFkEntry={1}, entry={2}", dFkEntry == entry ? "are" : "are not", dFkEntry.EntityKey.ConcatKeyValue(), entry.EntityKey.ConcatKeyValue()));
503                 }
504             }
505         }
506
507         [ConditionalAttribute("DEBUG")]
508         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Justification = "This method is compiled only when the compilation symbol DEBUG is defined")]
509         internal void AssertAllForeignKeyIndexEntriesAreValid()
510         {
511             if (_danglingForeignKeys.Count == 0)
512             {
513                 return;
514             }
515
516             HashSet<ObjectStateEntry> validEntries = new HashSet<ObjectStateEntry>(GetObjectStateEntriesInternal(~EntityState.Detached));
517             foreach (var entry in _danglingForeignKeys.SelectMany(kv => kv.Value))
518             {
519                 Debug.Assert(entry._cache != null, "found an entry in the _danglingForeignKeys collection that has been nulled out");
520                 Debug.Assert(validEntries.Contains(entry), "The entry in the dangling foreign key store is no longer in the ObjectStateManager. Key=" + (entry.State == EntityState.Detached ? "detached" : entry.EntityKey != null ? "null" : entry.EntityKey.ConcatKeyValue()));
521                 Debug.Assert(entry.State == EntityState.Detached || !ForeignKeyFactory.IsConceptualNullKey(entry.EntityKey), "Found an entry with conceptual null Key=" + entry.EntityKey.ConcatKeyValue());
522             }
523         }
524
525         /// <summary>
526         /// Removes an entry to the index of foreign keys that reference entities that we don't yet know about.
527         /// This is typically done when the entity is detached from the context.
528         /// </summary>
529         /// <param name="foreignKey">The foreign key found in the entry</param>
530         /// <param name="entry">The entry that contains the foreign key that was found</param>
531         internal void RemoveEntryFromForeignKeyIndex(EntityKey foreignKey, EntityEntry entry)
532         {
533             HashSet<EntityEntry> danglingEntries;
534             if (_danglingForeignKeys.TryGetValue(foreignKey, out danglingEntries))
535             {
536                 danglingEntries.Remove(entry);
537             }
538         }
539
540         /// <summary>
541         /// Removes the foreign key from the index of those keys that have been found in entries
542         /// but for which it was not possible to do fixup because the entity that the foreign key
543         /// referenced was not in the state manager.
544         /// </summary>
545         /// <param name="foreignKey">The key to lookup and remove</param>
546         internal void RemoveForeignKeyFromIndex(EntityKey foreignKey)
547         {
548             _danglingForeignKeys.Remove(foreignKey);
549         }
550
551         /// <summary>
552         /// Gets all state entries that contain the given foreign key for which we have not performed
553         /// fixup because the state manager did not contain the entity to which the foreign key pointed.
554         /// </summary>
555         /// <param name="foreignKey">The key to lookup</param>
556         /// <returns>The state entries that contain the key</returns>
557         internal IEnumerable<EntityEntry> GetNonFixedupEntriesContainingForeignKey(EntityKey foreignKey)
558         {
559             HashSet<EntityEntry> foundEntries;
560             if (_danglingForeignKeys.TryGetValue(foreignKey, out foundEntries))
561             {
562                 // these entries will be updated by the code consuming them, so 
563                 // create a stable container to iterate over.
564                 return foundEntries.ToList();
565             }
566             return Enumerable.Empty<EntityEntry>();
567         }
568
569         /// <summary>
570         /// Adds to index of currently tracked entities that have FK values that are conceptually
571         /// null but not actually null because the FK properties are not nullable.
572         /// If this index is non-empty in AcceptAllChanges or SaveChanges, then we throw.
573         /// If AcceptChanges is called on an entity and that entity is in the index, then
574         /// we will throw.
575         /// Note that the index is keyed by EntityEntry reference because it's only ever used
576         /// when we have the EntityEntry and this makes it slightly faster than using key lookup.
577         /// </summary>
578         internal void RememberEntryWithConceptualNull(EntityEntry entry)
579         {
580             if (_entriesWithConceptualNulls == null)
581             {
582                 _entriesWithConceptualNulls = new HashSet<EntityEntry>();
583             }
584             _entriesWithConceptualNulls.Add(entry);
585         }
586
587         /// <summary>
588         /// Checks whether or not there is some entry in the context that has any conceptually but not
589         /// actually null FK values.
590         /// </summary>
591         internal bool SomeEntryWithConceptualNullExists()
592         {
593             return _entriesWithConceptualNulls != null && _entriesWithConceptualNulls.Count != 0;
594         }
595
596         /// <summary>
597         /// Checks whether the given entry has conceptually but not actually null FK values.
598         /// </summary>
599         internal bool EntryHasConceptualNull(EntityEntry entry)
600         {
601             return _entriesWithConceptualNulls != null && _entriesWithConceptualNulls.Contains(entry);
602         }
603
604         /// <summary>
605         /// Stops keeping track of an entity with conceptual nulls because the FK values have been
606         /// really set or because the entity is leaving the context or becoming deleted.
607         /// </summary>
608         internal void ForgetEntryWithConceptualNull(EntityEntry entry, bool resetAllKeys)
609         {
610             if (!entry.IsKeyEntry && _entriesWithConceptualNulls != null && _entriesWithConceptualNulls.Remove(entry))
611             {
612                 if (entry.RelationshipManager.HasRelationships)
613                 {
614                     foreach (RelatedEnd end in entry.RelationshipManager.Relationships)
615                     {
616                         EntityReference reference = end as EntityReference;
617                         if (reference != null && ForeignKeyFactory.IsConceptualNullKey(reference.CachedForeignKey))
618                         {
619                             if (resetAllKeys)
620                             {
621                                 reference.SetCachedForeignKey(null, null);
622                             }
623                             else
624                             {
625                                 // This means that we thought we could remove because one FK was no longer conceptually
626                                 // null, but in fact we have to add the entry back because another FK is still conceptually null
627                                 _entriesWithConceptualNulls.Add(entry);
628                                 break;
629                             }
630                         }
631                     }
632                 }
633             }
634         }
635
636         // devnote: see comment to SQLBU 555615 in ObjectContext.AttachSingleObject()
637         internal void PromoteKeyEntryInitialization(ObjectContext contextToAttach, 
638             EntityEntry keyEntry,
639             IEntityWrapper wrappedEntity,
640             IExtendedDataRecord shadowValues,            
641             bool replacingEntry)
642         {
643             Debug.Assert(keyEntry != null, "keyEntry must be non-null.");
644             Debug.Assert(wrappedEntity != null, "entity cannot be null.");
645             // shadowValues is allowed to be null
646
647             // Future Enhancement: Fixup already has this information, don't rediscover it
648             StateManagerTypeMetadata typeMetadata = GetOrAddStateManagerTypeMetadata(wrappedEntity.IdentityType, (EntitySet)keyEntry.EntitySet);
649             ValidateProxyType(wrappedEntity);
650             keyEntry.PromoteKeyEntry(wrappedEntity, shadowValues, typeMetadata);
651             AddEntryToKeylessStore(keyEntry);
652
653             if (replacingEntry)
654             {
655                 // if we are replacing an existing entry, then clean the entity's change tracker
656                 // so that it can be reset to this newly promoted entry
657                 wrappedEntity.SetChangeTracker(null);
658             }
659             // A new entity is being added.
660             wrappedEntity.SetChangeTracker(keyEntry);
661
662             if (contextToAttach != null)
663             {
664                 // The ObjectContext needs to be attached to the wrapper here because we need it to be attached to
665                 // RelatedEnds for the snapshot change tracking that happens in TakeSnapshot. However, it
666                 // cannot be attached in ObjectContext.AttachSingleObject before calling this method because this
667                 // would attach it to RelatedEnds before SetChangeTracker is called, thereby breaking a legacy
668                 // case for entities derived from EntityObject--see AttachSingleObject for details.
669                 wrappedEntity.AttachContext(contextToAttach, (EntitySet)keyEntry.EntitySet, MergeOption.AppendOnly);
670             }
671
672             wrappedEntity.TakeSnapshot(keyEntry);
673
674             OnObjectStateManagerChanged(CollectionChangeAction.Add, keyEntry.Entity);
675         }
676
677         /// <summary>
678         /// Upgrades an entity key entry in the cache to a a regular entity
679         /// </summary>
680         /// <param name="keyEntry">the key entry that exists in the state manager</param>
681         /// <param name="entity">the object to add</param>
682         /// <param name="shadowValues">a data record representation of the entity's values, including any values in shadow state</param>
683         /// <param name="replacingEntry">True if this promoted key entry is replacing an existing detached entry</param>
684         /// <param name="setIsLoaded">Tells whether we should allow the IsLoaded flag to be set to true for RelatedEnds</param>
685         /// <param name="argumentName">Name of the argument passed to a public method, for use in exceptions.</param>
686         internal void PromoteKeyEntry(EntityEntry keyEntry,
687             IEntityWrapper wrappedEntity,
688             IExtendedDataRecord shadowValues,
689             bool replacingEntry,
690             bool setIsLoaded,
691             bool keyEntryInitialized,
692             string argumentName)
693         {
694             Debug.Assert(keyEntry != null, "keyEntry must be non-null.");
695             Debug.Assert(wrappedEntity != null, "entity wrapper cannot be null.");
696             Debug.Assert(wrappedEntity.Entity != null, "entity cannot be null.");
697             Debug.Assert(wrappedEntity.Context != null, "the context should be already set");
698             // shadowValues is allowed to be null
699             Debug.Assert(argumentName != null, "argumentName cannot be null.");
700
701             if (!keyEntryInitialized)
702             {
703                 // We pass null as the context here because, as asserted above, the context is already attached
704                 // to the wrapper when it comes down this path.
705                 this.PromoteKeyEntryInitialization(null, keyEntry, wrappedEntity, shadowValues, replacingEntry);
706             }
707
708             bool doCleanup = true;
709             try
710             {
711                 // We don't need to worry about the KeyEntry <-- Relationship --> KeyEntry because a key entry must
712                 // reference a non-key entry. Fix up their other side of the relationship.
713                 // Get all the relationships that currently exist for this key entry
714                 foreach (RelationshipEntry relationshipEntry in CopyOfRelationshipsByKey(keyEntry.EntityKey))
715                 {
716                     if (relationshipEntry.State != EntityState.Deleted)
717                     {
718                         // Find the association ends that correspond to the source and target
719                         AssociationEndMember sourceMember = keyEntry.GetAssociationEndMember(relationshipEntry);
720                         AssociationEndMember targetMember = MetadataHelper.GetOtherAssociationEnd(sourceMember);
721
722                         // Find the other end of the relationship
723                         EntityEntry targetEntry = keyEntry.GetOtherEndOfRelationship(relationshipEntry);
724
725                         // Here we are promoting based on a non-db retrieval so we use Append rules
726                         AddEntityToCollectionOrReference(
727                             MergeOption.AppendOnly,
728                             wrappedEntity,
729                             sourceMember,
730                             targetEntry.WrappedEntity,
731                             targetMember,
732                             /*setIsLoaded*/ setIsLoaded,
733                             /*relationshipAlreadyExists*/ true,
734                             /*inKeyEntryPromotion*/ true);
735                     }
736                 }
737                 FixupReferencesByForeignKeys(keyEntry);
738                 doCleanup = false;
739             }
740             finally
741             {
742                 if (doCleanup)
743                 {
744                     keyEntry.DetachObjectStateManagerFromEntity();
745                     RemoveEntryFromKeylessStore(wrappedEntity);
746                     keyEntry.DegradeEntry();
747                 }
748             }
749
750             if (this.TransactionManager.IsAttachTracking)
751             {
752                 this.TransactionManager.PromotedKeyEntries.Add(wrappedEntity.Entity, keyEntry);
753             }
754         }
755
756         internal void TrackPromotedRelationship(RelatedEnd relatedEnd, IEntityWrapper wrappedEntity)
757         {
758             Debug.Assert(relatedEnd != null);
759             Debug.Assert(wrappedEntity != null);
760             Debug.Assert(wrappedEntity.Entity != null);
761             Debug.Assert(this.TransactionManager.IsAttachTracking || this.TransactionManager.IsAddTracking, "This method should be called only from ObjectContext.AttachTo/AddObject (indirectly)");
762
763             IList<IEntityWrapper> entities;
764             if (!this.TransactionManager.PromotedRelationships.TryGetValue(relatedEnd, out entities))
765             {
766                 entities = new List<IEntityWrapper>();
767                 this.TransactionManager.PromotedRelationships.Add(relatedEnd, entities);
768             }
769             entities.Add(wrappedEntity);
770         }
771
772         internal void DegradePromotedRelationships()
773         {
774             Debug.Assert(this.TransactionManager.IsAttachTracking || this.TransactionManager.IsAddTracking, "This method should be called only from the cleanup code");
775
776             foreach (var pair in this.TransactionManager.PromotedRelationships)
777             {
778                 foreach (IEntityWrapper wrappedEntity in pair.Value)
779                 {
780                     if (pair.Key.RemoveFromCache(wrappedEntity, /*resetIsLoaded*/ false, /*preserveForeignKey*/ false))
781                     {
782                         pair.Key.OnAssociationChanged(CollectionChangeAction.Remove, wrappedEntity.Entity);
783                     }
784                 }
785             }
786         }
787
788         /// <summary>
789         /// Performs non-generic collection or reference fixup between two entities
790         /// This method should only be used in scenarios where we are automatically hooking up relationships for
791         /// the user, and not in cases where they are manually setting relationships.
792         /// </summary>
793         /// <param name="mergeOption">The MergeOption to use to decide how to resolve EntityReference conflicts</param>
794         /// <param name="sourceEntity">The entity instance on the source side of the relationship</param>
795         /// <param name="sourceMember">The AssociationEndMember that contains the metadata for the source entity</param>
796         /// <param name="targetEntity">The entity instance on the source side of the relationship</param>
797         /// <param name="targetMember">The AssociationEndMember that contains the metadata for the target entity</param>
798         /// <param name="setIsLoaded">Tells whether we should allow the IsLoaded flag to be set to true for RelatedEnds</param>
799         /// <param name="relationshipAlreadyExists">Whether or not the relationship entry already exists in the cache for these entities</param>
800         /// <param name="inKeyEntryPromotion">Whether this method is used in key entry promotion</param>
801         internal static void AddEntityToCollectionOrReference(
802             MergeOption mergeOption,
803             IEntityWrapper wrappedSource,
804             AssociationEndMember sourceMember,
805             IEntityWrapper wrappedTarget,
806             AssociationEndMember targetMember,
807             bool setIsLoaded,
808             bool relationshipAlreadyExists,
809             bool inKeyEntryPromotion)
810         {
811             // Call GetRelatedEnd to retrieve the related end on the source entity that points to the target entity
812             RelatedEnd relatedEnd = wrappedSource.RelationshipManager.GetRelatedEndInternal(sourceMember.DeclaringType.FullName, targetMember.Name);
813
814             // EntityReference can only have one value
815             if (targetMember.RelationshipMultiplicity != RelationshipMultiplicity.Many)
816             {
817                 Debug.Assert(relatedEnd is EntityReference, "If end is not Many multiplicity, then the RelatedEnd should be an EntityReference.");
818                 var relatedReference = (EntityReference)relatedEnd;
819
820                 switch (mergeOption)
821                 {
822                     case MergeOption.NoTracking:
823                         // if using NoTracking, we have no way of determining identity resolution.
824                         // Throw an exception saying the EntityReference is already populated and to try using
825                         // a different MergeOption
826                         Debug.Assert(relatedEnd.IsEmpty(), "This can occur when objects are loaded using a NoTracking merge option. Try using a different merge option when loading objects.");
827                         break;
828                     case MergeOption.AppendOnly:
829                         // SQLBU 551031
830                         // In key entry promotion case, detect that sourceEntity is already related to some entity in the context,
831                         // so it cannot be related to another entity being attached (relation 1-1).
832                         // Without this check we would throw exception from RelatedEnd.Add() but the exception message couldn't
833                         // properly describe what has happened.
834                         if (inKeyEntryPromotion &&
835                             !relatedReference.IsEmpty() &&
836                             !Object.ReferenceEquals(relatedReference.ReferenceValue.Entity, wrappedTarget.Entity))
837                         {
838                             throw EntityUtil.EntityConflictsWithKeyEntry();
839                         }
840                         break;
841
842                     case MergeOption.PreserveChanges:
843                     case MergeOption.OverwriteChanges:
844                         // Retrieve the current target entity and the relationship
845                         var currentWrappedTarget = relatedReference.ReferenceValue;
846
847                         // currentWrappedTarget may already be correct because we may already have done FK fixup as part of
848                         // accepting changes in the overwrite code.
849                         if (currentWrappedTarget != null && currentWrappedTarget.Entity != null && currentWrappedTarget != wrappedTarget)
850                         {
851                             // The source entity is already related to a different target, so before we hook it up to the new target,
852                             // disconnect the existing related ends and detach the relationship entry
853                             RelationshipEntry relationshipEntry = relatedEnd.FindRelationshipEntryInObjectStateManager(currentWrappedTarget);
854                             Debug.Assert(relationshipEntry != null || relatedEnd.IsForeignKey, "Could not find relationship entry for LAT relationship.");
855
856                             relatedEnd.RemoveAll();
857
858                             if (relationshipEntry != null)
859                             {
860                                 Debug.Assert(relationshipEntry != null, "Could not find relationship entry.");
861                                 // If the relationship was Added prior to the above RemoveAll, it will have already been detached
862                                 // If it was Unchanged, it is now Deleted and should be detached
863                                 // It should never have been Deleted before now, because we just got currentTargetEntity from the related end
864                                 if (relationshipEntry.State == EntityState.Deleted)
865                                 {
866                                     relationshipEntry.AcceptChanges();
867                                 }
868
869                                 Debug.Assert(relationshipEntry.State == EntityState.Detached, "relationshipEntry should be Detached");
870                             }
871                         }
872                         break;
873                 }
874             }
875
876             RelatedEnd targetRelatedEnd = null;
877             if (mergeOption == MergeOption.NoTracking)
878             {
879                 targetRelatedEnd = relatedEnd.GetOtherEndOfRelationship(wrappedTarget);
880                 if (targetRelatedEnd.IsLoaded)
881                 {
882                     // The EntityCollection has already been loaded as part of the query and adding additional
883                     // entities would cause duplicate entries
884                     throw EntityUtil.CannotFillTryDifferentMergeOption(targetRelatedEnd.SourceRoleName, targetRelatedEnd.RelationshipName);
885                 }
886             }
887
888             // we may have already retrieved the target end above, but if not, just get it now
889             if (targetRelatedEnd == null)
890             {
891                 targetRelatedEnd = relatedEnd.GetOtherEndOfRelationship(wrappedTarget);
892             }
893
894             // Add the target entity
895             relatedEnd.Add(wrappedTarget,
896                 applyConstraints: true,
897                 addRelationshipAsUnchanged: true,
898                 relationshipAlreadyExists: relationshipAlreadyExists,
899                 allowModifyingOtherEndOfRelationship: true,
900                 forceForeignKeyChanges: true);
901
902             Debug.Assert(!(inKeyEntryPromotion && wrappedSource.Context == null),
903                 "sourceEntity has been just attached to the context in PromoteKeyEntry, so Context shouldn't be null");
904             Debug.Assert(
905                 !(inKeyEntryPromotion &&
906                 wrappedSource.Context.ObjectStateManager.TransactionManager.IsAttachTracking &&
907                 (setIsLoaded || mergeOption == MergeOption.NoTracking)),
908                 "This verifies that UpdateRelatedEnd is a no-op in a keyEntryPromotion case when the method is called indirectly from ObjectContext.AttachTo");
909
910             // If either end is an EntityReference, we may need to set IsLoaded or the DetachedEntityKey
911             UpdateRelatedEnd(relatedEnd, wrappedSource, wrappedTarget, setIsLoaded, mergeOption);
912             UpdateRelatedEnd(targetRelatedEnd, wrappedTarget, wrappedSource, setIsLoaded, mergeOption);
913
914             // In case the method was called from ObjectContext.AttachTo, we have to track relationships which were "promoted"
915             // Tracked relationships are used in recovery code of AttachTo.
916             if (inKeyEntryPromotion && wrappedSource.Context.ObjectStateManager.TransactionManager.IsAttachTracking)
917             {
918                 wrappedSource.Context.ObjectStateManager.TrackPromotedRelationship(relatedEnd, wrappedTarget);
919                 wrappedSource.Context.ObjectStateManager.TrackPromotedRelationship(targetRelatedEnd, wrappedSource);
920             }
921         }
922
923         // devnote: This method should only be used in scenarios where we are automatically hooking up relationships for
924         // the user, and not in cases where they are manually setting relationships.
925         private static void UpdateRelatedEnd(RelatedEnd relatedEnd, IEntityWrapper wrappedEntity, IEntityWrapper wrappedRelatedEntity, bool setIsLoaded, MergeOption mergeOption)
926         {
927             AssociationEndMember endMember = (AssociationEndMember)(relatedEnd.ToEndMember);
928
929             if ((endMember.RelationshipMultiplicity == RelationshipMultiplicity.One ||
930                  endMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne))
931             {
932                 if (setIsLoaded)
933                 {
934                     relatedEnd.SetIsLoaded(true);
935                 }
936                 // else we just want to leave IsLoaded alone, not set it to false
937
938                 // In NoTracking cases, we want to enable the EntityReference.EntityKey property, so we have to set the key
939                 if (mergeOption == MergeOption.NoTracking)
940                 {
941                     EntityKey targetKey = wrappedRelatedEntity.EntityKey;
942                     EntityUtil.CheckEntityKeyNull(targetKey);
943
944                     // since endMember is not Many, relatedEnd must be an EntityReference
945                     ((EntityReference)relatedEnd).DetachedEntityKey = targetKey;
946                 }
947             }
948         }
949
950         /// <summary>
951         /// Updates the relationships between a given source entity and a collection of target entities.
952         /// Used for full span and related end Load methods, where the following may be true:
953         /// (a) both sides of each relationship are always full entities and not stubs
954         /// (b) there could be multiple entities to process at once
955         /// (c) NoTracking queries are possible.
956         /// Not used for relationship span because although some of the logic is similar, the above are not true.
957         /// </summary>
958         /// <param name="context">ObjectContext to use to look up existing relationships. Using the context here instead of ObjectStateManager because for NoTracking queries
959         /// we shouldn't even touch the state manager at all, so we don't want to access it until we know we are not using NoTracking.</param>
960         /// <param name="mergeOption">MergeOption to use when updating existing relationships</param>
961         /// <param name="associationSet">AssociationSet for the relationships</param>
962         /// <param name="sourceMember">Role of sourceEntity in associationSet</param>
963         /// <param name="sourceKey">EntityKey for sourceEntity</param>
964         /// <param name="sourceEntity">Source entity in the relationship</param>
965         /// <param name="targetMember">Role of each targetEntity in associationSet</param>
966         /// <param name="targetEntities">List of target entities to use to create relationships with sourceEntity</param>
967         /// <param name="setIsLoaded">Tells whether we should allow the IsLoaded flag to be set to true for RelatedEnds</param>
968         internal static int UpdateRelationships(ObjectContext context, MergeOption mergeOption, AssociationSet associationSet, AssociationEndMember sourceMember, EntityKey sourceKey, IEntityWrapper wrappedSource, AssociationEndMember targetMember, IList targets, bool setIsLoaded)
969         {
970             int count = 0;
971
972             context.ObjectStateManager.TransactionManager.BeginGraphUpdate();
973             try
974             {
975                 if (targets != null)
976                 {
977                     if (mergeOption == MergeOption.NoTracking)
978                     {
979                         RelatedEnd relatedEnd = wrappedSource.RelationshipManager.GetRelatedEndInternal(sourceMember.DeclaringType.FullName, targetMember.Name);
980                         if (!relatedEnd.IsEmpty())
981                         {
982                             // The RelatedEnd has already been filled as part of the query and adding additional
983                             // entities would cause duplicate entries
984                             throw EntityUtil.CannotFillTryDifferentMergeOption(relatedEnd.SourceRoleName, relatedEnd.RelationshipName);
985                         }
986                     }
987
988                     foreach (object someTarget in targets)
989                     {
990                         IEntityWrapper wrappedTarget = someTarget as IEntityWrapper;
991                         if (wrappedTarget == null)
992                         {
993                             wrappedTarget = EntityWrapperFactory.WrapEntityUsingContext(someTarget, context);
994                         }
995                         count++;
996
997                         // If there is an existing relationship entry, update it based on its current state and the MergeOption, otherwise add a new one            
998                         EntityState newEntryState;
999                         if (mergeOption == MergeOption.NoTracking)
1000                         {
1001                             // For NoTracking, we shouldn't touch the state manager, so no need to look for existing relationships to handle, just connect the two entities.
1002                             // We don't care if the relationship already exists in the state manager or not, so just pass relationshipAlreadyExists=true so it won't look for it
1003                             AddEntityToCollectionOrReference(
1004                                 MergeOption.NoTracking,
1005                                 wrappedSource,
1006                                 sourceMember,
1007                                 wrappedTarget,
1008                                 targetMember,
1009                                 setIsLoaded,
1010                                 /*relationshipAlreadyExists*/ true,
1011                                 /*inKeyEntryPromotion*/ false);
1012                         }
1013                         else
1014                         {
1015                             ObjectStateManager manager = context.ObjectStateManager;
1016                             EntityKey targetKey = wrappedTarget.EntityKey;
1017                             if (!TryUpdateExistingRelationships(context, mergeOption, associationSet, sourceMember, sourceKey, wrappedSource, targetMember, targetKey, setIsLoaded, out newEntryState))
1018                             {
1019                                 bool needNewRelationship = true;
1020                                 switch (sourceMember.RelationshipMultiplicity)
1021                                 {
1022                                     case RelationshipMultiplicity.ZeroOrOne:
1023                                     case RelationshipMultiplicity.One:
1024                                         // The other end of the relationship might already be related to something else, in which case we need to fix it up.
1025                                         // devnote1: In some cases we can let relationship span do this, but there are cases, like EntityCollection.Attach, where there is no query
1026                                         //           and thus no relationship span to help us. So, for now, this is redundant because relationship span will make another pass over these
1027                                         //           entities, but unless I add a flag or something to indicate when I have to do it and when I don't, this is necessary.
1028                                         // devnote2: The target and source arguments are intentionally reversed in the following call, because we already know there isn't a relationship
1029                                         //           between the two entities we are current processing, but we want to see if there is one between the target and another source
1030                                         needNewRelationship = !TryUpdateExistingRelationships(context, mergeOption, associationSet, targetMember,
1031                                             targetKey, wrappedTarget, sourceMember, sourceKey, setIsLoaded, out newEntryState);
1032                                         break;
1033                                     case RelationshipMultiplicity.Many:
1034                                         // we always need a new relationship with Many-To-Many, if there was no exact match between these two entities, so do nothing                                
1035                                         break;
1036                                     default:
1037                                         Debug.Assert(false, "Unexpected sourceMember.RelationshipMultiplicity");
1038                                         break;
1039                                 }
1040                                 if (needNewRelationship)
1041                                 {
1042                                     if (newEntryState != EntityState.Deleted)
1043                                     {
1044                                         AddEntityToCollectionOrReference(
1045                                             mergeOption,
1046                                             wrappedSource,
1047                                             sourceMember,
1048                                             wrappedTarget,
1049                                             targetMember,
1050                                             setIsLoaded,
1051                                             /*relationshipAlreadyExists*/ false,
1052                                             /*inKeyEntryPromotion*/ false);
1053                                     }
1054                                     else
1055                                     {
1056                                         // Add a Deleted relationship between the source entity and the target entity
1057                                         // No end fixup is necessary since the relationship is Deleted
1058                                         RelationshipWrapper wrapper = new RelationshipWrapper(associationSet, sourceMember.Name, sourceKey, targetMember.Name, targetKey);
1059                                         manager.AddNewRelation(wrapper, EntityState.Deleted);
1060                                     }
1061                                 }
1062                                 // else there is nothing else for us to do, the relationship has been handled already
1063                             }
1064                             // else there is nothing else for us to do, the relationship has been handled already
1065                         }
1066                     }
1067                 }
1068                 if (count == 0)
1069                 {
1070                     // If we didn't put anything into the collection, then at least make sure that it is empty
1071                     // rather than null.
1072                     EnsureCollectionNotNull(sourceMember, wrappedSource, targetMember);
1073                 }
1074             }
1075             finally
1076             {
1077                 context.ObjectStateManager.TransactionManager.EndGraphUpdate();
1078             }
1079             return count;
1080             // devnote: Don't set IsLoaded on the target related end here -- the caller can do this more efficiently than we can here in some cases.
1081         }
1082
1083         // Checks if the target end is a collection and, if so, ensures that it is not
1084         // null by creating an empty collection if necessary.
1085         private static void EnsureCollectionNotNull(AssociationEndMember sourceMember, IEntityWrapper wrappedSource, AssociationEndMember targetMember)
1086         {
1087             RelatedEnd relatedEnd = wrappedSource.RelationshipManager.GetRelatedEndInternal(sourceMember.DeclaringType.FullName, targetMember.Name);
1088             AssociationEndMember endMember = (AssociationEndMember)(relatedEnd.ToEndMember);
1089             if (endMember != null && endMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
1090             {
1091                 if (relatedEnd.TargetAccessor.HasProperty)
1092                 {
1093                     wrappedSource.EnsureCollectionNotNull(relatedEnd);
1094                 }
1095             }
1096         }
1097
1098         /// <summary>
1099         /// Removes relationships if necessary when a query determines that the source entity has no relationships on the server
1100         /// </summary>
1101         /// <param name="context">ObjectContext that contains the client relationships</param>
1102         /// <param name="mergeOption">MergeOption to use when updating existing relationships</param>
1103         /// <param name="associationSet">AssociationSet for the incoming relationship</param>
1104         /// <param name="sourceKey">EntityKey of the source entity in the relationship</param>
1105         /// <param name="sourceMember">Role of the source entity in the relationship</param>
1106         internal static void RemoveRelationships(ObjectContext context, MergeOption mergeOption, AssociationSet associationSet,
1107             EntityKey sourceKey, AssociationEndMember sourceMember)
1108         {
1109             Debug.Assert(mergeOption == MergeOption.PreserveChanges || mergeOption == MergeOption.OverwriteChanges, "Unexpected MergeOption");
1110             // Initial capacity is set to avoid an almost immediate resizing, which was causing a perf hit.
1111             List<RelationshipEntry> deletedRelationships = new List<RelationshipEntry>(_initialListSize);
1112
1113             // This entity has no related entities on the server for the given associationset and role. If it has related
1114             // entities on the client, we may need to update those relationships, depending on the MergeOption
1115             if (mergeOption == MergeOption.OverwriteChanges)
1116             {
1117                 foreach (RelationshipEntry relationshipEntry in context.ObjectStateManager.FindRelationshipsByKey(sourceKey))
1118                 {
1119                     // We only care about the relationships that match the incoming associationset and role for the source entity
1120                     if (relationshipEntry.IsSameAssociationSetAndRole(associationSet, sourceMember, sourceKey))
1121                     {
1122                         deletedRelationships.Add(relationshipEntry);
1123                     }
1124                 }
1125             }
1126             else if (mergeOption == MergeOption.PreserveChanges)
1127             {
1128                 // Leave any Added relationships for this entity, but remove Unchanged and Deleted ones
1129                 foreach (RelationshipEntry relationshipEntry in context.ObjectStateManager.FindRelationshipsByKey(sourceKey))
1130                 {
1131                     // We only care about the relationships that match the incoming associationset and role for the source entity
1132                     if (relationshipEntry.IsSameAssociationSetAndRole(associationSet, sourceMember, sourceKey) &&
1133                         relationshipEntry.State != EntityState.Added)
1134                     {
1135                         deletedRelationships.Add(relationshipEntry);
1136                     }
1137                 }
1138             }
1139             // else we do nothing. We never expect any other states here, and already Assert this condition at the top of the method
1140
1141             foreach (RelationshipEntry deletedEntry in deletedRelationships)
1142             {
1143                 ObjectStateManager.RemoveRelatedEndsAndDetachRelationship(deletedEntry, true);
1144             }
1145         }
1146         /// <summary>
1147         /// Tries to updates one or more existing relationships for an entity, based on a given MergeOption and a target entity. 
1148         /// </summary>
1149         /// <param name="context">ObjectContext to use to look up existing relationships for sourceEntity</param>
1150         /// <param name="mergeOption">MergeOption to use when updating existing relationships</param>
1151         /// <param name="associationSet">AssociationSet for the relationship we are looking for</param>
1152         /// <param name="sourceMember">AssociationEndMember for the source role of the relationship</param>
1153         /// <param name="sourceKey">EntityKey for the source entity in the relationship (passed here so we don't have to look it up again)</param>
1154         /// <param name="sourceEntity">Source entity in the relationship</param>
1155         /// <param name="targetMember">AssociationEndMember for the target role of the relationship</param>
1156         /// <param name="targetKey">EntityKey for the target entity in the relationship</param>    
1157         /// <param name="setIsLoaded">Tells whether we should allow the IsLoaded flag to be set to true for RelatedEnds</param>
1158         /// <param name="newEntryState">[out] EntityState to be used for in scenarios where we need to add a new relationship after this method has returned</param>  
1159         /// <returns>
1160         /// true if an existing relationship is found and updated, and no further action is needed
1161         /// false if either no relationship was found, or if one was found and updated, but a new one still needs to be added
1162         /// </returns>
1163         internal static bool TryUpdateExistingRelationships(ObjectContext context, MergeOption mergeOption, AssociationSet associationSet, AssociationEndMember sourceMember, EntityKey sourceKey, IEntityWrapper wrappedSource, AssociationEndMember targetMember, EntityKey targetKey, bool setIsLoaded, out EntityState newEntryState)
1164         {
1165             Debug.Assert(mergeOption != MergeOption.NoTracking, "Existing relationships should not be updated with NoTracking");
1166
1167             // New relationships are always added as Unchanged except in specific scenarios. If there are multiple relationships being updated, and
1168             // at least one of those requests the new relationship to be Deleted, it should always be added as Deleted, even if there are other
1169             // relationships being updated that don't specify a state. Adding as Unchanged is just the default unless a scenario needs it to be Deleted to 
1170             // achieve a particular result.
1171             newEntryState = EntityState.Unchanged;
1172             // FK full span for tracked entities is handled entirely by FK fix up in the state manager.
1173             // Therefore, if the relationship is a FK, we just return indicating that nothing is to be done.
1174             if (associationSet.ElementType.IsForeignKey)
1175             {
1176                 return true;
1177             }
1178             // Unless we find a case below where we explicitly do not want a new relationship, we should always add one to match the server.
1179             bool needNewRelationship = true;
1180
1181             ObjectStateManager manager = context.ObjectStateManager;
1182             List<RelationshipEntry> entriesToDetach = null;
1183             List<RelationshipEntry> entriesToUpdate = null;
1184             foreach (RelationshipEntry relationshipEntry in manager.FindRelationshipsByKey(sourceKey))
1185             {
1186                 // We only care about relationships for the same AssociationSet and where the source entity is in the same role as it is in the incoming relationship.
1187                 if (relationshipEntry.IsSameAssociationSetAndRole(associationSet, sourceMember, sourceKey))
1188                 {
1189                     // If the other end of this relationship matches our current target entity, this relationship entry matches the server
1190                     if (targetKey == relationshipEntry.RelationshipWrapper.GetOtherEntityKey(sourceKey))
1191                     {
1192                         if (entriesToUpdate == null)
1193                         {
1194                             // Initial capacity is set to avoid an almost immediate resizing, which was causing a perf hit.
1195                             entriesToUpdate = new List<RelationshipEntry>(_initialListSize);
1196                         }
1197                         entriesToUpdate.Add(relationshipEntry);
1198                     }
1199                     else
1200                     {
1201                         // We found an existing relationship where the reference side is different on the server than what the client has.
1202
1203                         // This relationship is between the same source entity and a different target, so we may need to take steps to fix up the 
1204                         // relationship to ensure that the client state is correct based on the requested MergeOption. 
1205                         // The only scenario we care about here is one where the target member has zero or one multiplicity (0..1 or 1..1), because those
1206                         // are the only cases where it is meaningful to say that the relationship is different on the server and the client. In scenarios
1207                         // where the target member has a many (*) multiplicity, it is possible to have multiple relationships between the source key
1208                         // and other entities, and we don't want to touch those here.
1209                         switch (targetMember.RelationshipMultiplicity)
1210                         {
1211                             case RelationshipMultiplicity.One:
1212                             case RelationshipMultiplicity.ZeroOrOne:
1213                                 switch (mergeOption)
1214                                 {
1215                                     case MergeOption.AppendOnly:
1216                                         if (relationshipEntry.State != EntityState.Deleted)
1217                                         {
1218                                             Debug.Assert(relationshipEntry.State == EntityState.Added || relationshipEntry.State == EntityState.Unchanged, "Unexpected relationshipEntry state");
1219                                             needNewRelationship = false; // adding a new relationship would conflict with the existing one
1220                                         }
1221                                         break;
1222                                     case MergeOption.OverwriteChanges:
1223                                         if (entriesToDetach == null)
1224                                         {
1225                                             // Initial capacity is set to avoid an almost immediate resizing, which was causing a perf hit.
1226                                             entriesToDetach = new List<RelationshipEntry>(_initialListSize);
1227                                         }
1228                                         entriesToDetach.Add(relationshipEntry);
1229                                         break;
1230                                     case MergeOption.PreserveChanges:
1231                                         switch (relationshipEntry.State)
1232                                         {
1233                                             case EntityState.Added:
1234                                                 newEntryState = EntityState.Deleted;
1235                                                 break;
1236                                             case EntityState.Unchanged:
1237                                                 if (entriesToDetach == null)
1238                                                 {
1239                                                     // Initial capacity is set to avoid an almost immediate resizing, which was causing a perf hit.
1240                                                     entriesToDetach = new List<RelationshipEntry>(_initialListSize);
1241                                                 }
1242                                                 entriesToDetach.Add(relationshipEntry);
1243                                                 break;
1244                                             case EntityState.Deleted:
1245                                                 newEntryState = EntityState.Deleted;
1246                                                 if (entriesToDetach == null)
1247                                                 {
1248                                                     // Initial capacity is set to avoid an almost immediate resizing, which was causing a perf hit.
1249                                                     entriesToDetach = new List<RelationshipEntry>(_initialListSize);
1250                                                 }
1251                                                 entriesToDetach.Add(relationshipEntry);
1252                                                 break;
1253                                             default:
1254                                                 Debug.Assert(false, "Unexpected relationship entry state");
1255                                                 break;
1256                                         }
1257                                         break;
1258                                     default:
1259                                         Debug.Assert(false, "Unexpected MergeOption");
1260                                         break;
1261                                 }
1262                                 break;
1263                             case RelationshipMultiplicity.Many:
1264                                 // do nothing because its okay for this source entity to have multiple different targets, so there is nothing for us to fixup
1265                                 break;
1266                             default:
1267                                 Debug.Assert(false, "Unexpected targetMember.RelationshipMultiplicity");
1268                                 break;
1269                         }
1270                     }
1271                 }
1272             }
1273
1274             // Detach all of the entries that we have collected above
1275             if (entriesToDetach != null)
1276             {
1277                 foreach (RelationshipEntry entryToDetach in entriesToDetach)
1278                 {
1279                     // the entry may have already been detached by another operation. If not, detach it now.
1280                     if (entryToDetach.State != EntityState.Detached)
1281                     {
1282                         RemoveRelatedEndsAndDetachRelationship(entryToDetach, setIsLoaded);
1283                     }
1284                 }
1285             }
1286
1287             // Update all of the matching entries that we have collectioned above
1288             if (entriesToUpdate != null)
1289             {
1290                 foreach (RelationshipEntry relationshipEntry in entriesToUpdate)
1291                 {
1292                     // Don't need new relationship to be added to match the server, since we already have a match
1293                     needNewRelationship = false;
1294
1295                     // We have an existing relationship entry that matches exactly to the incoming relationship from the server, but
1296                     // we may need to update it on the client based on the MergeOption and the state of the relationship entry.
1297                     switch (mergeOption)
1298                     {
1299                         case MergeOption.AppendOnly:
1300                             // AppendOnly and NoTracking shouldn't affect existing relationships, so do nothing
1301                             break;
1302                         case MergeOption.OverwriteChanges:
1303                             if (relationshipEntry.State == EntityState.Added)
1304                             {
1305                                 relationshipEntry.AcceptChanges();
1306                             }
1307                             else if (relationshipEntry.State == EntityState.Deleted)
1308                             {
1309                                 // targetEntry should always exist in this scenario because it would have
1310                                 // at least been created when the relationship entry was created
1311                                 EntityEntry targetEntry = manager.GetEntityEntry(targetKey);
1312
1313                                 // If the target entity is deleted, we don't want to bring the relationship entry back.                            
1314                                 if (targetEntry.State != EntityState.Deleted)
1315                                 {
1316                                     // If the targetEntry is a KeyEntry, there are no ends to fix up.
1317                                     if (!targetEntry.IsKeyEntry)
1318                                     {
1319                                         ObjectStateManager.AddEntityToCollectionOrReference(
1320                                             mergeOption,
1321                                             wrappedSource,
1322                                             sourceMember,
1323                                             targetEntry.WrappedEntity,
1324                                             targetMember,
1325                                             /*setIsLoaded*/ setIsLoaded,
1326                                             /*relationshipAlreadyExists*/ true,
1327                                             /*inKeyEntryPromotion*/ false);
1328                                     }
1329                                     relationshipEntry.RevertDelete();
1330                                 }
1331                             }
1332                             // else it's already Unchanged so we don't need to do anything
1333                             break;
1334                         case MergeOption.PreserveChanges:
1335                             if (relationshipEntry.State == EntityState.Added)
1336                             {
1337                                 // The client now matches the server, so just move the relationship to unchanged.
1338                                 // If we don't do this and left the state Added, we will get a concurrency exception when trying to save
1339                                 relationshipEntry.AcceptChanges();
1340                             }
1341                             // else if it's already Unchanged we don't need to do anything
1342                             // else if it's Deleted we want to preserve that state so do nothing
1343                             break;
1344                         default:
1345                             Debug.Assert(false, "Unexpected MergeOption");
1346                             break;
1347                     }
1348                 }
1349             }
1350             return !needNewRelationship;
1351         }
1352
1353         // Helper method to disconnect two related ends and detach their associated relationship entry
1354         internal static void RemoveRelatedEndsAndDetachRelationship(RelationshipEntry relationshipToRemove, bool setIsLoaded)
1355         {
1356             // If we are allowed to set the IsLoaded flag, then we can consider unloading these relationships
1357             if (setIsLoaded)
1358             {
1359                 // If the relationship needs to be deleted, then we should unload the related ends
1360                 UnloadReferenceRelatedEnds(relationshipToRemove);
1361             }
1362
1363             // Delete the relationship entry and disconnect the related ends
1364             if (relationshipToRemove.State != EntityState.Deleted)
1365             {
1366                 relationshipToRemove.Delete();
1367             }
1368
1369             // Detach the relationship entry
1370             // Entries that were in the Added state prior to the Delete above will have already been Detached
1371             if (relationshipToRemove.State != EntityState.Detached)
1372             {
1373                 relationshipToRemove.AcceptChanges();
1374             }
1375         }
1376
1377         private static void UnloadReferenceRelatedEnds(RelationshipEntry relationshipEntry)
1378         {
1379             //Find two ends of the relationship
1380             ObjectStateManager cache = relationshipEntry.ObjectStateManager;
1381             ReadOnlyMetadataCollection<AssociationEndMember> endMembers = relationshipEntry.RelationshipWrapper.AssociationEndMembers;
1382
1383             UnloadReferenceRelatedEnds(cache, relationshipEntry, relationshipEntry.RelationshipWrapper.GetEntityKey(0), endMembers[1].Name);
1384             UnloadReferenceRelatedEnds(cache, relationshipEntry, relationshipEntry.RelationshipWrapper.GetEntityKey(1), endMembers[0].Name);
1385         }
1386
1387         private static void UnloadReferenceRelatedEnds(ObjectStateManager cache, RelationshipEntry relationshipEntry, EntityKey sourceEntityKey, string targetRoleName)
1388         {
1389             EntityEntry entry = cache.GetEntityEntry(sourceEntityKey);
1390
1391             if (entry.WrappedEntity.Entity != null)
1392             {
1393                 EntityReference reference = entry.WrappedEntity.RelationshipManager.GetRelatedEndInternal(((AssociationSet)relationshipEntry.EntitySet).ElementType.FullName, targetRoleName) as EntityReference;
1394                 if (reference != null)
1395                 {
1396                     reference.SetIsLoaded(false);
1397                 }
1398             }
1399         }
1400
1401         /// <summary>
1402         /// Attach entity in unchanged state (skip Added state, don't create temp key)
1403         /// It is equal (but faster) to call AddEntry(); AcceptChanges().
1404         /// </summary>
1405         /// <param name="entity"></param>
1406         /// <param name="entitySet"></param>
1407         /// <param name="argumentName"></param>
1408         internal EntityEntry AttachEntry(EntityKey entityKey, IEntityWrapper wrappedObject, EntitySet entitySet, string argumentName)
1409         {
1410             Debug.Assert(wrappedObject != null, "entity wrapper cannot be null.");
1411             Debug.Assert(wrappedObject.Entity != null, "entity cannot be null.");
1412             Debug.Assert(wrappedObject.Context != null, "the context should be already set");
1413             Debug.Assert(entitySet != null, "entitySet must be non-null.");
1414             Debug.Assert(argumentName != null, "argumentName cannot be null.");
1415             Debug.Assert(entityKey != null, "argumentName cannot be null.");
1416
1417             // Get a StateManagerTypeMetadata for the entity type.
1418             StateManagerTypeMetadata typeMetadata = GetOrAddStateManagerTypeMetadata(wrappedObject.IdentityType, entitySet);
1419
1420             ValidateProxyType(wrappedObject);
1421
1422             CheckKeyMatchesEntity(wrappedObject, entityKey, entitySet, /*forAttach*/ true);
1423
1424             if (!wrappedObject.OwnsRelationshipManager)
1425             {
1426                 // When a POCO instance is added or attached, we need to ignore the contents 
1427                 // of the RelationshipManager as it is out-of-date with the POCO nav props
1428                 wrappedObject.RelationshipManager.ClearRelatedEndWrappers();
1429             }
1430
1431             // Create a cache entry.
1432             EntityEntry newEntry = new EntityEntry(wrappedObject, entityKey, entitySet, this, typeMetadata, EntityState.Unchanged);
1433
1434             // The property EntityKey on newEntry validates that the entry and the entity on the entry have the same key.
1435             Debug.Assert(entityKey == newEntry.EntityKey, "newEntry.EntityKey should match entityKey");
1436
1437             // A entity is being attached.
1438             newEntry.AttachObjectStateManagerToEntity();
1439             AddEntityEntryToDictionary(newEntry, newEntry.State);
1440
1441             // fire ColectionChanged event only when a new entity is added to cache
1442             OnObjectStateManagerChanged(CollectionChangeAction.Add, newEntry.Entity);
1443
1444             return newEntry;
1445         }
1446
1447         /// <summary>
1448         /// Checks that the EntityKey attached to the given entity
1449         /// appropriately matches the given entity.
1450         /// </summary>
1451         /// <param name="entity">The entity whose key must be verified</param>
1452         /// <param name="entitySetForType">The entity set corresponding to the type of the given entity.</param>
1453         /// <param name="forAttach">If true, then the exception message will reflect a bad key to attach, otherwise it will reflect a general inconsistency</param>
1454         private void CheckKeyMatchesEntity(IEntityWrapper wrappedEntity, EntityKey entityKey, EntitySet entitySetForType, bool forAttach)
1455         {
1456             Debug.Assert(wrappedEntity != null, "Cannot verify key for null entity wrapper.");
1457             Debug.Assert(wrappedEntity.Entity != null, "Cannot verify key for null entity.");
1458
1459             Debug.Assert((object)entityKey != null, "Cannot verify a null EntityKey.");
1460             Debug.Assert(!entityKey.IsTemporary, "Verifying a temporary EntityKey doesn't make sense because the key doesn't contain any values.");
1461             Debug.Assert(entitySetForType != null, "Cannot verify against a null entity set.");
1462
1463             EntitySet entitySetForKey = entityKey.GetEntitySet(this.MetadataWorkspace);
1464             if (entitySetForKey == null)
1465             {
1466                 throw EntityUtil.InvalidKey();
1467             }
1468
1469             // Checks that the entity's key matches its type.
1470             Debug.Assert(entitySetForType.Name == entitySetForKey.Name &&
1471                          entitySetForType.EntityContainer.Name == entitySetForKey.EntityContainer.Name,
1472                          "The object cannot be attached because its EntityType belongs to a different EntitySet than the one specified in its key.");
1473
1474             // Verify that the entity key contains the correct members for the entity set
1475             entityKey.ValidateEntityKey(_metadataWorkspace, entitySetForKey);
1476
1477             // Checks that the key values in the entity match the key values
1478             // within its EntityKey.
1479             StateManagerTypeMetadata typeMetadata = GetOrAddStateManagerTypeMetadata(wrappedEntity.IdentityType, entitySetForType);
1480             for (int i = 0; i < entitySetForKey.ElementType.KeyMembers.Count; ++i)
1481             {
1482                 EdmMember keyField = entitySetForKey.ElementType.KeyMembers[i];
1483                 int ordinal = typeMetadata.GetOrdinalforCLayerMemberName(keyField.Name);
1484                 if (ordinal < 0)
1485                 {
1486                     throw EntityUtil.InvalidKey();
1487                 }
1488
1489                 object entityValue = typeMetadata.Member(ordinal).GetValue(wrappedEntity.Entity);
1490                 object keyValue = entityKey.FindValueByName(keyField.Name);
1491
1492                 // Use EntityKey.ValueComparer to perform the correct equality comparison for entity key values.
1493                 if (!ByValueEqualityComparer.Default.Equals(entityValue, keyValue))
1494                 {
1495                     throw EntityUtil.KeyPropertyDoesntMatchValueInKey(forAttach);
1496                 }
1497             }
1498         }
1499
1500         internal RelationshipEntry AddNewRelation(RelationshipWrapper wrapper, EntityState desiredState)
1501         {
1502             Debug.Assert(null == FindRelationship(wrapper), "relationship should not exist, caller verifies");
1503
1504             RelationshipEntry entry = new RelationshipEntry(this, desiredState, wrapper);
1505             AddRelationshipEntryToDictionary(entry, desiredState);
1506             AddRelationshipToLookup(entry);
1507             return entry;
1508         }
1509
1510         internal RelationshipEntry AddRelation(RelationshipWrapper wrapper, EntityState desiredState)
1511         {
1512             Debug.Assert(EntityState.Added == desiredState ||        // result entry should be added or left alone
1513                          EntityState.Unchanged == desiredState ||    // result entry should be that state
1514                          EntityState.Deleted == desiredState,        // result entry should be in that state
1515                          "unexpected state");
1516
1517             RelationshipEntry entry = FindRelationship(wrapper);
1518             Debug.Assert(null == entry || (EntityState.Modified != entry.State), "relationship should never be modified");
1519
1520             if (entry == null)
1521             {
1522                 entry = AddNewRelation(wrapper, desiredState);
1523             }
1524             else if (EntityState.Deleted != entry.State)
1525             {
1526                 // you can have a deleted and non-deleted relation between two entities
1527                 // SQL BU DT 449757: for now no-op in case if it exists. ideally need to throw
1528                 if (EntityState.Unchanged == desiredState)
1529                 {
1530                     entry.AcceptChanges();
1531                 }
1532                 else if (EntityState.Deleted == desiredState)
1533                 {
1534                     entry.AcceptChanges();
1535                     entry.Delete(false);
1536                 }
1537                 // else Added and leave entry alone
1538             }
1539             else if (EntityState.Deleted != desiredState)
1540             {
1541                 Debug.Assert(EntityState.Deleted == entry.State, "should be deleted state");
1542                 entry.RevertDelete();
1543             }
1544             // else entry already Deleted or if desired state is Added then left alone
1545
1546             Debug.Assert(desiredState == entry.State ||
1547                          EntityState.Added == desiredState,
1548                          "unexpected end state");
1549             Debug.Assert(entry is RelationshipEntry, "unexpected type of entry");
1550             return (RelationshipEntry)entry;
1551         }
1552
1553         /// <summary>
1554         /// Adds the given relationship cache entry to the mapping from each of its endpoint keys.
1555         /// </summary>
1556         private void AddRelationshipToLookup(RelationshipEntry relationship)
1557         {
1558             Debug.Assert(relationship != null, "relationship can't be null");
1559
1560             AddRelationshipEndToLookup(relationship.RelationshipWrapper.Key0, relationship);
1561             if (!relationship.RelationshipWrapper.Key0.Equals(relationship.RelationshipWrapper.Key1))
1562             {
1563                 AddRelationshipEndToLookup(relationship.RelationshipWrapper.Key1, relationship);
1564             }
1565         }
1566
1567         /// <summary>
1568         /// Adds the given relationship cache entry to the mapping from the given endpoint key.
1569         /// </summary>
1570         private void AddRelationshipEndToLookup(EntityKey key, RelationshipEntry relationship)
1571         {
1572             Debug.Assert(null != FindEntityEntry(key), "EntityEntry doesn't exist");
1573
1574             EntityEntry entry = GetEntityEntry(key);
1575             Debug.Assert(key.Equals(entry.EntityKey), "EntityKey mismatch");
1576             entry.AddRelationshipEnd(relationship);
1577         }
1578
1579         /// <summary>
1580         /// Deletes the given relationship cache entry from the mapping from each of its endpoint keys.
1581         /// </summary>
1582         private void DeleteRelationshipFromLookup(RelationshipEntry relationship)
1583         {
1584             // The relationship is stored in the lookup indexed by both keys, so we need to remove it twice.
1585             DeleteRelationshipEndFromLookup(relationship.RelationshipWrapper.Key0, relationship);
1586             if (!relationship.RelationshipWrapper.Key0.Equals(relationship.RelationshipWrapper.Key1))
1587             {
1588                 DeleteRelationshipEndFromLookup(relationship.RelationshipWrapper.Key1, relationship);
1589             }
1590         }
1591
1592         /// <summary>
1593         /// Deletes the given relationship cache entry from the mapping from the given endpoint key.
1594         /// </summary>
1595         private void DeleteRelationshipEndFromLookup(EntityKey key, RelationshipEntry relationship)
1596         {
1597             Debug.Assert(relationship.State != EntityState.Detached, "Cannot remove a detached cache entry.");
1598             Debug.Assert(null != FindEntityEntry(key), "EntityEntry doesn't exist");
1599
1600             EntityEntry entry = GetEntityEntry(key);
1601             Debug.Assert(key.Equals(entry.EntityKey), "EntityKey mismatch");
1602             entry.RemoveRelationshipEnd(relationship);
1603         }
1604
1605         internal RelationshipEntry FindRelationship(RelationshipSet relationshipSet,
1606                                                    KeyValuePair<string, EntityKey> roleAndKey1,
1607                                                    KeyValuePair<string, EntityKey> roleAndKey2)
1608         {
1609             if ((null == (object)roleAndKey1.Value) || (null == (object)roleAndKey2.Value))
1610             {
1611                 return null;
1612             }
1613             return FindRelationship(new RelationshipWrapper((AssociationSet)relationshipSet, roleAndKey1, roleAndKey2));
1614         }
1615
1616         internal RelationshipEntry FindRelationship(RelationshipWrapper relationshipWrapper)
1617         {
1618             RelationshipEntry entry = null;
1619             bool result = (((null != _unchangedRelationshipStore) && _unchangedRelationshipStore.TryGetValue(relationshipWrapper, out  entry)) ||
1620                            ((null != _deletedRelationshipStore) && _deletedRelationshipStore.TryGetValue(relationshipWrapper, out  entry)) ||
1621                            ((null != _addedRelationshipStore) && _addedRelationshipStore.TryGetValue(relationshipWrapper, out entry)));
1622             Debug.Assert(result == (null != entry), "found null entry");
1623             return entry;
1624         }
1625
1626         /// <summary>
1627         /// DeleteRelationship
1628         /// </summary>
1629         /// <returns>The deleted entry</returns>
1630         internal RelationshipEntry DeleteRelationship(RelationshipSet relationshipSet,
1631                                           KeyValuePair<string, EntityKey> roleAndKey1,
1632                                           KeyValuePair<string, EntityKey> roleAndKey2)
1633         {
1634             RelationshipEntry entry = FindRelationship(relationshipSet, roleAndKey1, roleAndKey2);
1635             if (entry != null)
1636             {
1637                 entry.Delete(/*doFixup*/ false);
1638             }
1639             return entry;
1640         }
1641
1642
1643         /// <summary>
1644         /// DeleteKeyEntry
1645         /// </summary>
1646         internal void DeleteKeyEntry(EntityEntry keyEntry)
1647         {
1648             if (keyEntry != null && keyEntry.IsKeyEntry)
1649             {
1650                 ChangeState(keyEntry, keyEntry.State, EntityState.Detached);
1651             }
1652         }
1653
1654
1655         /// <summary>
1656         /// Finds all relationships with the given key at one end.
1657         /// </summary>
1658         internal RelationshipEntry[] CopyOfRelationshipsByKey(EntityKey key)
1659         {
1660             return FindRelationshipsByKey(key).ToArray();
1661         }
1662
1663         /// <summary>
1664         /// Finds all relationships with the given key at one end.
1665         /// Do not use the list to add elements
1666         /// </summary>
1667         internal EntityEntry.RelationshipEndEnumerable FindRelationshipsByKey(EntityKey key)
1668         {
1669             return new EntityEntry.RelationshipEndEnumerable((EntityEntry)FindEntityEntry(key));
1670         }
1671
1672         IEnumerable<IEntityStateEntry> IEntityStateManager.FindRelationshipsByKey(EntityKey key)
1673         {
1674             return FindRelationshipsByKey(key);
1675         }
1676
1677         //Verify that all entities in the _keylessEntityStore are also in the other dictionaries.
1678         //Verify that all the entries in the _keylessEntityStore don't implement IEntityWithKey.
1679         //Verify that there no entries in the other dictionaries that don't implement IEntityWithKey and aren't in _keylessEntityStore
1680         [ConditionalAttribute("DEBUG")]
1681         private void ValidateKeylessEntityStore()
1682         {
1683             // Future Enhancement : Check each entry in _keylessEntityStore to make sure it has a corresponding entry in one of the other stores.
1684             if (null != _keylessEntityStore)
1685             {
1686                 foreach (EntityEntry entry in _keylessEntityStore.Values)
1687                 {
1688                     Debug.Assert(!(entry.Entity is IEntityWithKey), "_keylessEntityStore contains an entry that implement IEntityWithKey");
1689                     EntityEntry entrya;
1690                     bool result = false;
1691                     if (null != _addedEntityStore)
1692                     {
1693                         result = _addedEntityStore.TryGetValue(entry.EntityKey, out entrya);
1694                     }
1695                     if (null != _modifiedEntityStore)
1696                     {
1697                         result |= _modifiedEntityStore.TryGetValue(entry.EntityKey, out entrya);
1698                     }
1699                     if (null != _deletedEntityStore)
1700                     {
1701                         result |= _deletedEntityStore.TryGetValue(entry.EntityKey, out entrya);
1702                     }
1703                     if (null != _unchangedEntityStore)
1704                     {
1705                         result |= _unchangedEntityStore.TryGetValue(entry.EntityKey, out entrya);
1706                     }
1707                     Debug.Assert(result, "entry in _keylessEntityStore doesn't exist in one of the other stores");
1708                 }
1709             }
1710
1711             //Check each entry in the other stores to make sure that each non-IEntityWithKey entry is also in _keylessEntityStore
1712             Dictionary<EntityKey, EntityEntry>[] stores = { _unchangedEntityStore, _modifiedEntityStore, _addedEntityStore, _deletedEntityStore };
1713             foreach (Dictionary<EntityKey, EntityEntry> store in stores)
1714             {
1715                 if (null != store)
1716                 {
1717                     foreach (EntityEntry entry in store.Values)
1718                     {
1719                         if (null != entry.Entity && //Skip span stub key entry
1720                             !(entry.Entity is IEntityWithKey))
1721                         {
1722                             EntityEntry keylessEntry;
1723                             Debug.Assert(null != _keylessEntityStore, "There should be a store that keyless entries are in");
1724                             if (_keylessEntityStore.TryGetValue(entry.Entity, out keylessEntry))
1725                             {
1726                                 Debug.Assert(object.ReferenceEquals(entry, keylessEntry), "keylessEntry and entry from stores do not match");
1727                             }
1728                             else
1729                             {
1730                                 Debug.Assert(false, "The entry containing an entity not implementing IEntityWithKey is not in the _keylessEntityStore");
1731                             }
1732                         }
1733                     }
1734                 }
1735             }
1736         }
1737         /// <summary>
1738         /// Find the ObjectStateEntry from _keylessEntityStore for an entity that doesn't implement IEntityWithKey.
1739         /// </summary>
1740         /// <param name="entity"></param>
1741         /// <returns></returns>
1742         private bool TryGetEntryFromKeylessStore(object entity, out EntityEntry entryRef)
1743         {
1744             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
1745             Debug.Assert(!(entity is IEntityWithKey));
1746
1747             ValidateKeylessEntityStore();
1748             entryRef = null;
1749             if (entity == null)
1750             {
1751                 return false;
1752             }
1753             if (null != _keylessEntityStore)
1754             {
1755                 if (_keylessEntityStore.TryGetValue(entity, out entryRef))
1756                 {
1757                     return true;
1758                 }
1759             }
1760
1761             entryRef = null;
1762             return false;
1763         }
1764
1765         /// <summary>
1766         /// Returns all CacheEntries in the given state.
1767         /// </summary>
1768         /// <exception cref="ArgumentException">if EntityState.Detached flag is set in state</exception>
1769         public IEnumerable<ObjectStateEntry> GetObjectStateEntries(EntityState state)
1770         {
1771             if ((EntityState.Detached & state) != 0)
1772             {
1773                 throw EntityUtil.DetachedObjectStateEntriesDoesNotExistInObjectStateManager();
1774             }
1775             return GetObjectStateEntriesInternal(state);
1776         }
1777
1778         /// <summary>
1779         /// Returns all CacheEntries in the given state.
1780         /// </summary>
1781         /// <exception cref="ArgumentException">if EntityState.Detached flag is set in state</exception>
1782         IEnumerable<IEntityStateEntry> IEntityStateManager.GetEntityStateEntries(EntityState state)
1783         {
1784             Debug.Assert((EntityState.Detached & state) == 0, "Cannot get state entries for detached entities");
1785             foreach (ObjectStateEntry stateEntry in GetObjectStateEntriesInternal(state))
1786             {
1787                 yield return (IEntityStateEntry)stateEntry;
1788             }
1789         }
1790
1791         internal int GetObjectStateEntriesCount(EntityState state)
1792         {
1793             int size = 0;
1794             if ((EntityState.Added & state) != 0)
1795             {
1796                 size += ((null != _addedRelationshipStore) ? _addedRelationshipStore.Count : 0);
1797                 size += ((null != _addedEntityStore) ? _addedEntityStore.Count : 0);
1798             }
1799             if ((EntityState.Modified & state) != 0)
1800             {
1801                 size += ((null != _modifiedEntityStore) ? _modifiedEntityStore.Count : 0);
1802             }
1803             if ((EntityState.Deleted & state) != 0)
1804             {
1805                 size += ((null != _deletedRelationshipStore) ? _deletedRelationshipStore.Count : 0);
1806                 size += ((null != _deletedEntityStore) ? _deletedEntityStore.Count : 0);
1807             }
1808             if ((EntityState.Unchanged & state) != 0)
1809             {
1810                 size += ((null != _unchangedRelationshipStore) ? _unchangedRelationshipStore.Count : 0);
1811                 size += ((null != _unchangedEntityStore) ? _unchangedEntityStore.Count : 0);
1812             }
1813             return size;
1814         }
1815
1816         private int GetMaxEntityEntriesForDetectChanges()
1817         {
1818             int size = 0;
1819             if (_addedEntityStore != null)
1820             {
1821                 size += _addedEntityStore.Count;
1822             }
1823             if (_modifiedEntityStore != null)
1824             {
1825                 size += _modifiedEntityStore.Count;
1826             }
1827             if (_deletedEntityStore != null)
1828             {
1829                 size += _deletedEntityStore.Count;
1830             }
1831             if (_unchangedEntityStore != null)
1832             {
1833                 size += _unchangedEntityStore.Count;
1834             }
1835             return size;
1836         }
1837
1838         private ObjectStateEntry[] GetObjectStateEntriesInternal(EntityState state)
1839         {
1840             Debug.Assert((EntityState.Detached & state) == 0, "Cannot get state entries for detached entities");
1841
1842             int size = GetObjectStateEntriesCount(state);
1843             ObjectStateEntry[] entries = new ObjectStateEntry[size];
1844
1845             size = 0; // size is now used as an offset
1846             if (((EntityState.Added & state) != 0) && (null != _addedRelationshipStore))
1847             {
1848                 foreach (var e in _addedRelationshipStore)
1849                 {
1850                     entries[size++] = e.Value;
1851                 }
1852             }
1853             if (((EntityState.Deleted & state) != 0) && (null != _deletedRelationshipStore))
1854             {
1855                 foreach (var e in _deletedRelationshipStore)
1856                 {
1857                     entries[size++] = e.Value;
1858                 }
1859             }
1860             if (((EntityState.Unchanged & state) != 0) && (null != _unchangedRelationshipStore))
1861             {
1862                 foreach (var e in _unchangedRelationshipStore)
1863                 {
1864                     entries[size++] = e.Value;
1865                 }
1866             }
1867             if (((EntityState.Added & state) != 0) && (null != _addedEntityStore))
1868             {
1869                 foreach (var e in _addedEntityStore)
1870                 {
1871                     entries[size++] = e.Value;
1872                 }
1873             }
1874             if (((EntityState.Modified & state) != 0) && (null != _modifiedEntityStore))
1875             {
1876                 foreach (var e in _modifiedEntityStore)
1877                 {
1878                     entries[size++] = e.Value;
1879                 }
1880             }
1881             if (((EntityState.Deleted & state) != 0) && (null != _deletedEntityStore))
1882             {
1883                 foreach (var e in _deletedEntityStore)
1884                 {
1885                     entries[size++] = e.Value;
1886                 }
1887             }
1888             if (((EntityState.Unchanged & state) != 0) && (null != _unchangedEntityStore))
1889             {
1890                 foreach (var e in _unchangedEntityStore)
1891                 {
1892                     entries[size++] = e.Value;
1893                 }
1894             }
1895             return entries;
1896         }
1897
1898         private IList<EntityEntry> GetEntityEntriesForDetectChanges()
1899         {
1900             // This flag is set whenever an entity that may need snapshot change tracking
1901             // becomes tracked by the context.  Entities that may need snapshot change tracking
1902             // are those for which any of the following are true:
1903             // a) Entity does not implement IEntityWithRelationships
1904             // b) Entity does not implement IEntityWithChangeTracker
1905             // b) Entity has a complex property.
1906             if (!_detectChangesNeeded)
1907             {
1908                 return null;
1909             }
1910
1911             List<EntityEntry> entries = null; // Will be lazy initialized if needed.
1912             GetEntityEntriesForDetectChanges(_addedEntityStore, ref entries);
1913             GetEntityEntriesForDetectChanges(_modifiedEntityStore, ref entries);
1914             GetEntityEntriesForDetectChanges(_deletedEntityStore, ref entries);
1915             GetEntityEntriesForDetectChanges(_unchangedEntityStore, ref entries);
1916
1917             // If the flag was set, but we don't find anything to do, then reset the flag again
1918             // since it means that there were some entities that needed DetectChanges, but now they
1919             // have been detached.
1920             if (entries == null)
1921             {
1922                 _detectChangesNeeded = false;
1923             }
1924
1925             return entries;
1926         }
1927
1928         private void GetEntityEntriesForDetectChanges(Dictionary<EntityKey, EntityEntry> entityStore, ref List<EntityEntry> entries)
1929         {
1930             if (entityStore != null)
1931             {
1932                 foreach (var entry in entityStore.Values)
1933                 {
1934                     if (entry.RequiresAnyChangeTracking)
1935                     {
1936                         if (entries == null)
1937                         {
1938                             entries = new List<EntityEntry>(GetMaxEntityEntriesForDetectChanges());
1939                         }
1940                         entries.Add(entry);
1941                     }
1942                 }
1943             }
1944         }
1945
1946         #region temporary (added state) to permanent (deleted, modified, unchanged state) EntityKey fixup
1947
1948         /// <summary>
1949         /// Performs key-fixup on the given entry, by creating a (permanent) EntityKey
1950         /// based on the current key values within the associated entity and fixing up
1951         /// all associated relationship entries.
1952         /// </summary>
1953         /// <remarks>
1954         /// Will promote EntityEntry.IsKeyEntry and leave in _unchangedStore
1955         /// otherwise will move EntityEntry from _addedStore to _unchangedStore.
1956         /// </remarks>
1957         internal void FixupKey(EntityEntry entry)
1958         {
1959             Debug.Assert(entry != null, "entry should not be null.");
1960             Debug.Assert(entry.State == EntityState.Added, "Cannot do key fixup for an entry not in the Added state.");
1961             Debug.Assert(entry.Entity != null, "must have entity, can't be entity stub in added state");
1962
1963             EntityKey oldKey = entry.EntityKey;
1964             Debug.Assert(entry == _addedEntityStore[oldKey], "not the same EntityEntry");
1965             Debug.Assert((object)oldKey != null, "Cannot fixup a cache entry with a null key.");
1966             Debug.Assert(oldKey.IsTemporary, "Cannot fixup an entry with a non-temporary key.");
1967             Debug.Assert(null != _addedEntityStore, "missing added store");
1968
1969             var entitySet = (EntitySet)entry.EntitySet;
1970             bool performFkSteps = entitySet.HasForeignKeyRelationships;
1971             bool performNonFkSteps = entitySet.HasIndependentRelationships;
1972
1973             if (performFkSteps)
1974             {
1975                 // Do fixup based on reference first for added objects.
1976                 // This must be done before creating a new key or the key will have old values.
1977                 entry.FixupForeignKeysByReference();
1978             }
1979
1980             EntityKey newKey;
1981             try
1982             {
1983                 // Construct an EntityKey based on the current, fixed-up values of the entry.
1984                 newKey = new EntityKey((EntitySet)entry.EntitySet, (IExtendedDataRecord)entry.CurrentValues);
1985             }
1986             catch (ArgumentException ex)
1987             {
1988                 // ArgumentException is not the best choice here but anything else would be a breaking change.
1989                 throw new ArgumentException(System.Data.Entity.Strings.ObjectStateManager_ChangeStateFromAddedWithNullKeyIsInvalid, ex);
1990             }
1991
1992             EntityEntry existingEntry = FindEntityEntry(newKey);
1993             if (existingEntry != null)
1994             {
1995                 if (!existingEntry.IsKeyEntry)
1996                 {
1997                     // If the fixed-up key conflicts with an existing entry, we throw.
1998                     throw EntityUtil.CannotFixUpKeyToExistingValues();
1999                 }
2000                 newKey = existingEntry.EntityKey; // reuse existing reference
2001             }
2002
2003             RelationshipEntry[] relationshipEnds = null;
2004             if (performNonFkSteps)
2005             {
2006                 // remove the relationships based on the temporary key
2007                 relationshipEnds = entry.GetRelationshipEnds().ToArray();
2008                 foreach (RelationshipEntry relationshipEntry in relationshipEnds)
2009                 {
2010                     RemoveObjectStateEntryFromDictionary(relationshipEntry, relationshipEntry.State);
2011                 }
2012             }
2013
2014             // Remove ObjectStateEntry with old Key and add it back or promote with new key.
2015             RemoveObjectStateEntryFromDictionary(entry, EntityState.Added);
2016
2017             // This is the only scenario where we are allowed to set the EntityKey if it's already non-null
2018             // If entry.EntityKey is IEntityWithKey, user code will be called
2019             ResetEntityKey(entry, newKey);
2020
2021             if (performNonFkSteps)
2022             {
2023                 // Fixup all relationships for which this key was a participant.
2024                 entry.UpdateRelationshipEnds(oldKey, existingEntry);
2025
2026                 // add all the relationships back on the new entity key
2027                 foreach (RelationshipEntry relationshipEntry in relationshipEnds)
2028                 {
2029                     AddRelationshipEntryToDictionary(relationshipEntry, relationshipEntry.State);
2030                 }
2031             }
2032
2033             // Now promote the key entry to a full entry by adding entities to the related ends
2034             if (existingEntry != null)
2035             {
2036                 // two ObjectStateEntry exist for same newKey, the entity stub must exist in unchanged state
2037                 Debug.Assert(existingEntry.State == EntityState.Unchanged, "entity stub must be in unchanged state");
2038                 Debug.Assert(existingEntry.IsKeyEntry, "existing entry must be a key entry to promote");
2039                 Debug.Assert(Object.ReferenceEquals(newKey, existingEntry.EntityKey), "should be same key reference");
2040                 PromoteKeyEntry(existingEntry, entry.WrappedEntity, null, true, /*setIsLoaded*/ false, /*keyEntryInitialized*/ false, "AcceptChanges");
2041
2042                 // leave the entity stub in the unchanged state
2043                 // the existing entity stub wins
2044                 entry = existingEntry;
2045             }
2046             else
2047             {
2048                 // change the state to "Unchanged"
2049                 AddEntityEntryToDictionary(entry, EntityState.Unchanged);
2050             }
2051
2052             if (performFkSteps)
2053             {
2054                 FixupReferencesByForeignKeys(entry);
2055             }
2056
2057             Debug.Assert((null == _addedEntityStore) || !_addedEntityStore.ContainsKey(oldKey), "EntityEntry exists with OldKey");
2058             Debug.Assert((null != _unchangedEntityStore) && _unchangedEntityStore.ContainsKey(newKey), "EntityEntry does not exist with NewKey");
2059
2060             // FEATURE_CHANGE: once we support equality constraints (SQL PT DB 300002154), do recursive fixup.
2061         }
2062
2063         /// <summary>
2064         /// Replaces permanent EntityKey with a temporary key.  Used in N-Tier API.
2065         /// </summary>
2066         internal void ReplaceKeyWithTemporaryKey(EntityEntry entry)
2067         {
2068             Debug.Assert(entry != null, "entry should not be null.");
2069             Debug.Assert(entry.State != EntityState.Added, "Cannot replace key with a temporary key if the entry is in Added state.");
2070             Debug.Assert(!entry.IsKeyEntry, "Cannot replace a key of a KeyEntry");
2071
2072             EntityKey oldKey = entry.EntityKey;
2073             Debug.Assert(!oldKey.IsTemporary, "Entity is not in the Added state but has a temporary key.");
2074
2075             // Construct an temporary EntityKey.
2076             EntityKey newKey = new EntityKey(entry.EntitySet);
2077
2078             Debug.Assert(FindEntityEntry(newKey) == null, "no entry should exist with the new temporary key");
2079
2080             // remove the relationships based on the permanent key
2081             RelationshipEntry[] relationshipEnds = entry.GetRelationshipEnds().ToArray();
2082             foreach (RelationshipEntry relationshipEntry in relationshipEnds)
2083             {
2084                 RemoveObjectStateEntryFromDictionary(relationshipEntry, relationshipEntry.State);
2085             }
2086
2087             // Remove ObjectStateEntry with old Key and add it back or promote with new key.
2088             RemoveObjectStateEntryFromDictionary(entry, entry.State);
2089
2090             // This is the only scenario where we are allowed to set the EntityKey if it's already non-null
2091             // If entry.EntityKey is IEntityWithKey, user code will be called
2092             ResetEntityKey(entry, newKey);
2093
2094             // Fixup all relationships for which this key was a participant.
2095             entry.UpdateRelationshipEnds(oldKey, null); // null PromotedEntry
2096
2097             // add all the relationships back on the new entity key
2098             foreach (RelationshipEntry relationshipEntry in relationshipEnds)
2099             {
2100                 AddRelationshipEntryToDictionary(relationshipEntry, relationshipEntry.State);
2101             }
2102
2103             AddEntityEntryToDictionary(entry, EntityState.Added);
2104         }
2105
2106         /// <summary>
2107         /// Resets the EntityKey for this entry.  This method is called
2108         /// as part of temporary key fixup and permanent key un-fixup. This method is necessary because it is the only
2109         /// scenario where we allow a new value to be set on a non-null EntityKey. This
2110         /// is the only place where we should be setting and clearing _inRelationshipFixup.
2111         /// </summary>
2112         private void ResetEntityKey(EntityEntry entry, EntityKey value)
2113         {
2114             Debug.Assert((object)entry.EntityKey != null, "Cannot reset an entry's key if it hasn't been set in the first place.");
2115             Debug.Assert(!_inRelationshipFixup, "already _inRelationshipFixup");
2116             Debug.Assert(!entry.EntityKey.Equals(value), "the keys should not be equal");
2117
2118             EntityKey entityKey = entry.WrappedEntity.EntityKey;
2119             if (entityKey == null || value.Equals(entityKey))
2120             {
2121                 throw EntityUtil.AcceptChangesEntityKeyIsNotValid();
2122             }
2123             try
2124             {
2125                 _inRelationshipFixup = true;
2126                 entry.WrappedEntity.EntityKey = value; // user will have control
2127                 EntityUtil.CheckEntityKeysMatch(entry.WrappedEntity, value);
2128             }
2129             finally
2130             {
2131                 _inRelationshipFixup = false;
2132             }
2133
2134             // Keeping the entity and entry keys in sync.
2135             entry.EntityKey = value;
2136
2137             //Internally, entry.EntityKey asserts that entry._entityKey and entityWithKey.EntityKey are equal.
2138             Debug.Assert(value == entry.EntityKey, "The new key was not set onto the entry correctly");
2139         }
2140
2141         #endregion
2142
2143         /// <summary>
2144         /// Finds an ObjectStateEntry for the given entity and changes its state to the new state.
2145         /// The operation does not trigger cascade deletion.
2146         /// The operation may change state of adjacent relationships.
2147         /// </summary>
2148         /// <param name="entity">entity which state should be changed</param>
2149         /// <param name="entityState">new state of the entity</param>
2150         /// <returns>entry associated with entity</returns>
2151         public ObjectStateEntry ChangeObjectState(object entity, EntityState entityState)
2152         {
2153             EntityUtil.CheckArgumentNull(entity, "entity");
2154             EntityUtil.CheckValidStateForChangeEntityState(entityState);
2155
2156             EntityEntry entry = null;
2157
2158             this.TransactionManager.BeginLocalPublicAPI();
2159             try
2160             {
2161                 EntityKey key = entity as EntityKey;
2162                 entry = (key != null) ?
2163                     this.FindEntityEntry(key) :
2164                     this.FindEntityEntry(entity);
2165
2166                 if (entry == null)
2167                 {
2168                     if (entityState == EntityState.Detached)
2169                     {
2170                         return null; // No-op
2171                     }
2172                     throw EntityUtil.NoEntryExistsForObject(entity);
2173                 }
2174
2175                 entry.ChangeObjectState(entityState);
2176             }
2177             finally
2178             {
2179                 this.TransactionManager.EndLocalPublicAPI();
2180             }
2181
2182             return entry;
2183         }
2184
2185         /// <summary>
2186         /// Changes state of a relationship between two entities. 
2187         /// </summary>
2188         /// <remarks>
2189         /// Both entities must be already tracked by the ObjectContext.
2190         /// </remarks>
2191         /// <param name="sourceEntity">The instance of the source entity or the EntityKey of the source entity</param>
2192         /// <param name="targetEntity">The instance of the target entity or the EntityKey of the target entity</param>
2193         /// <param name="navigationProperty">The name of the navigation property on the source entity</param>
2194         /// <param name="relationshipState">The requested state of the relationship</param>
2195         /// <returns>The ObjectStateEntry for changed relationship</returns>
2196         public ObjectStateEntry ChangeRelationshipState(
2197             object sourceEntity,
2198             object targetEntity,
2199             string navigationProperty,
2200             EntityState relationshipState)
2201         {
2202             EntityEntry sourceEntry;
2203             EntityEntry targetEntry;
2204
2205             this.VerifyParametersForChangeRelationshipState(sourceEntity, targetEntity, out sourceEntry, out targetEntry);
2206             EntityUtil.CheckStringArgument(navigationProperty, "navigationProperty");
2207
2208             RelatedEnd relatedEnd = (RelatedEnd)sourceEntry.WrappedEntity.RelationshipManager.GetRelatedEnd(navigationProperty);
2209
2210             return this.ChangeRelationshipState(sourceEntry, targetEntry, relatedEnd, relationshipState);
2211         }
2212
2213         /// <summary>
2214         /// Changes state of a relationship between two entities.
2215         /// </summary>
2216         /// <remarks>
2217         /// Both entities must be already tracked by the ObjectContext.
2218         /// </remarks>
2219         /// <param name="sourceEntity">The instance of the source entity or the EntityKey of the source entity</param>
2220         /// <param name="targetEntity">The instance of the target entity or the EntityKey of the target entity</param>
2221         /// <param name="navigationPropertySelector">A LINQ expression specifying the navigation property</param>
2222         /// <param name="relationshipState">The requested state of the relationship</param>
2223         /// <returns>The ObjectStateEntry for changed relationship</returns>
2224         public ObjectStateEntry ChangeRelationshipState<TEntity>(
2225             TEntity sourceEntity,
2226             object targetEntity,
2227             Expression<Func<TEntity, object>> navigationPropertySelector,
2228             EntityState relationshipState) where TEntity : class
2229         {
2230             EntityEntry sourceEntry;
2231             EntityEntry targetEntry;
2232
2233             this.VerifyParametersForChangeRelationshipState(sourceEntity, targetEntity, out sourceEntry, out targetEntry);
2234
2235             // We used to throw an ArgumentException if the expression contained a Convert.  Now we remove the convert,
2236             // but if we still need to throw, then we should still throw an ArgumentException to avoid a breaking change.
2237             // Therefore, we keep track of whether or not we removed the convert.
2238             bool removedConvert;
2239             string navigationProperty = ObjectContext.ParsePropertySelectorExpression<TEntity>(navigationPropertySelector, out removedConvert);
2240             RelatedEnd relatedEnd = (RelatedEnd)sourceEntry.WrappedEntity.RelationshipManager.GetRelatedEnd(navigationProperty, throwArgumentException: removedConvert);
2241
2242             return this.ChangeRelationshipState(sourceEntry, targetEntry, relatedEnd, relationshipState);
2243         }
2244
2245         /// <summary>
2246         /// Changes state of a relationship between two entities.
2247         /// </summary>
2248         /// <remarks>
2249         /// Both entities must be already tracked by the ObjectContext.
2250         /// </remarks>
2251         /// <param name="sourceEntity">The instance of the source entity or the EntityKey of the source entity</param>
2252         /// <param name="targetEntity">The instance of the target entity or the EntityKey of the target entity</param>
2253         /// <param name="relationshipName">The name of relationship</param>
2254         /// <param name="targetRoleName">The target role name of the relationship</param>
2255         /// <param name="relationshipState">The requested state of the relationship</param>
2256         /// <returns>The ObjectStateEntry for changed relationship</returns>
2257         public ObjectStateEntry ChangeRelationshipState(
2258             object sourceEntity,
2259             object targetEntity,
2260             string relationshipName,
2261             string targetRoleName,
2262             EntityState relationshipState)
2263         {
2264             EntityEntry sourceEntry;
2265             EntityEntry targetEntry;
2266
2267             this.VerifyParametersForChangeRelationshipState(sourceEntity, targetEntity, out sourceEntry, out targetEntry);
2268
2269             RelatedEnd relatedEnd = sourceEntry.WrappedEntity.RelationshipManager.GetRelatedEndInternal(relationshipName, targetRoleName);
2270
2271             return this.ChangeRelationshipState(sourceEntry, targetEntry, relatedEnd, relationshipState);
2272         }
2273
2274         private ObjectStateEntry ChangeRelationshipState(
2275             EntityEntry sourceEntry,
2276             EntityEntry targetEntry,
2277             RelatedEnd relatedEnd,
2278             EntityState relationshipState)
2279         {
2280             VerifyInitialStateForChangeRelationshipState(sourceEntry, targetEntry, relatedEnd, relationshipState);
2281
2282             RelationshipWrapper relationshipWrapper = new RelationshipWrapper((AssociationSet)relatedEnd.RelationshipSet,
2283                 new KeyValuePair<string, EntityKey>(relatedEnd.SourceRoleName, sourceEntry.EntityKey),
2284                 new KeyValuePair<string, EntityKey>(relatedEnd.TargetRoleName, targetEntry.EntityKey));
2285
2286             RelationshipEntry relationshipEntry = this.FindRelationship(relationshipWrapper);
2287
2288             if (relationshipEntry == null && relationshipState == EntityState.Detached)
2289             {
2290                 // no-op
2291                 return null;
2292             }
2293
2294             this.TransactionManager.BeginLocalPublicAPI();
2295             try
2296             {
2297                 if (relationshipEntry != null)
2298                 {
2299                     relationshipEntry.ChangeRelationshipState(targetEntry, relatedEnd, relationshipState);
2300                 }
2301                 else
2302                 {
2303                     relationshipEntry = this.CreateRelationship(targetEntry, relatedEnd, relationshipWrapper, relationshipState);
2304                 }
2305             }
2306             finally
2307             {
2308                 this.TransactionManager.EndLocalPublicAPI();
2309             }
2310
2311             Debug.Assert(relationshipState != EntityState.Detached || relationshipEntry.State == EntityState.Detached, "state should be detached");
2312             return relationshipState == EntityState.Detached ? null : relationshipEntry;
2313         }
2314
2315         private void VerifyParametersForChangeRelationshipState(object sourceEntity, object targetEntity, out EntityEntry sourceEntry, out EntityEntry targetEntry)
2316         {
2317             EntityUtil.CheckArgumentNull(sourceEntity, "sourceEntity");
2318             EntityUtil.CheckArgumentNull(targetEntity, "targetEntity");
2319
2320             sourceEntry = this.GetEntityEntryByObjectOrEntityKey(sourceEntity);
2321             targetEntry = this.GetEntityEntryByObjectOrEntityKey(targetEntity);
2322         }
2323
2324         private void VerifyInitialStateForChangeRelationshipState(EntityEntry sourceEntry, EntityEntry targetEntry, RelatedEnd relatedEnd, EntityState relationshipState)
2325         {
2326             relatedEnd.VerifyType(targetEntry.WrappedEntity);
2327
2328             if (relatedEnd.IsForeignKey)
2329             {
2330                 throw new NotSupportedException(System.Data.Entity.Strings.ObjectStateManager_ChangeRelationshipStateNotSupportedForForeignKeyAssociations);
2331             }
2332
2333             EntityUtil.CheckValidStateForChangeRelationshipState(relationshipState, "relationshipState");
2334
2335             if ((sourceEntry.State == EntityState.Deleted || targetEntry.State == EntityState.Deleted) &&
2336                 (relationshipState != EntityState.Deleted && relationshipState != EntityState.Detached))
2337             {
2338                 throw new InvalidOperationException(System.Data.Entity.Strings.ObjectStateManager_CannotChangeRelationshipStateEntityDeleted);
2339             }
2340
2341             if ((sourceEntry.State == EntityState.Added || targetEntry.State == EntityState.Added) &&
2342                 (relationshipState != EntityState.Added && relationshipState != EntityState.Detached))
2343             {
2344                 throw new InvalidOperationException(System.Data.Entity.Strings.ObjectStateManager_CannotChangeRelationshipStateEntityAdded);
2345             }
2346         }
2347
2348         private RelationshipEntry CreateRelationship(EntityEntry targetEntry, RelatedEnd relatedEnd, RelationshipWrapper relationshipWrapper, EntityState requestedState)
2349         {
2350             Debug.Assert(requestedState != EntityState.Modified, "relationship cannot be in Modified state");
2351
2352             RelationshipEntry relationshipEntry = null;
2353
2354             switch (requestedState)
2355             {
2356                 case EntityState.Added:
2357                     relatedEnd.Add(targetEntry.WrappedEntity,
2358                         applyConstraints: true,
2359                         addRelationshipAsUnchanged: false,
2360                         relationshipAlreadyExists: false,
2361                         allowModifyingOtherEndOfRelationship: false,
2362                         forceForeignKeyChanges: true);
2363                     relationshipEntry = this.FindRelationship(relationshipWrapper);
2364                     Debug.Assert(relationshipEntry != null, "null relationshipEntry");
2365                     break;
2366                 case EntityState.Unchanged:
2367                     relatedEnd.Add(targetEntry.WrappedEntity,
2368                         applyConstraints: true,
2369                         addRelationshipAsUnchanged: false,
2370                         relationshipAlreadyExists: false,
2371                         allowModifyingOtherEndOfRelationship: false,
2372                         forceForeignKeyChanges: true);
2373                     relationshipEntry = this.FindRelationship(relationshipWrapper);
2374                     relationshipEntry.AcceptChanges();
2375                     break;
2376                 case EntityState.Deleted:
2377                     relationshipEntry = this.AddNewRelation(relationshipWrapper, EntityState.Deleted);
2378                     break;
2379                 case EntityState.Detached:
2380                     // no-op
2381                     break;
2382                 default:
2383                     Debug.Assert(false, "Invalid requested state");
2384                     break;
2385             }
2386
2387             return relationshipEntry;
2388         }
2389
2390
2391         private EntityEntry GetEntityEntryByObjectOrEntityKey(object o)
2392         {
2393             EntityKey key = o as EntityKey;
2394             EntityEntry entry = (key != null) ?
2395                 this.FindEntityEntry(key) :
2396                 this.FindEntityEntry(o);
2397
2398             if (entry == null)
2399             {
2400                 throw EntityUtil.NoEntryExistsForObject(o);
2401             }
2402
2403             if (entry.IsKeyEntry)
2404             {
2405                 throw new InvalidOperationException(System.Data.Entity.Strings.ObjectStateManager_CannotChangeRelationshipStateKeyEntry);
2406             }
2407
2408             return entry;
2409         }
2410
2411
2412         /// <summary>
2413         /// Retrieve the corresponding IEntityStateEntry for the given EntityKey.
2414         /// </summary>
2415         /// <exception cref="ArgumentNullException">if key is null</exception>
2416         /// <exception cref="ArgumentException">if key is not found</exception>
2417         IEntityStateEntry IEntityStateManager.GetEntityStateEntry(EntityKey key)
2418         {
2419             return (IEntityStateEntry)GetEntityEntry(key);
2420         }
2421
2422         /// <summary>
2423         /// Retrieve the corresponding ObjectStateEntry for the given EntityKey.
2424         /// </summary>
2425         /// <exception cref="ArgumentNullException">if key is null</exception>
2426         /// <exception cref="ArgumentException">if key is not found</exception>
2427         public ObjectStateEntry GetObjectStateEntry(EntityKey key)
2428         {
2429             ObjectStateEntry entry;
2430             if (!TryGetObjectStateEntry(key, out entry))
2431             {
2432                 throw EntityUtil.NoEntryExistForEntityKey();
2433             }
2434             return entry;
2435         }
2436
2437         internal EntityEntry GetEntityEntry(EntityKey key)
2438         {
2439             EntityEntry entry;
2440             if (!TryGetEntityEntry(key, out entry))
2441             {
2442                 throw EntityUtil.NoEntryExistForEntityKey();
2443             }
2444             return entry;
2445         }
2446
2447
2448         /// <summary>
2449         /// Given an entity, of type object, return the corresponding ObjectStateEntry.
2450         /// </summary>
2451         /// <param name="entity"></param>
2452         /// <returns>The corresponding ObjectStateEntry for this object.</returns>
2453         public ObjectStateEntry GetObjectStateEntry(object entity)
2454         {
2455             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
2456             ObjectStateEntry entry;
2457             if (!TryGetObjectStateEntry(entity, out entry))
2458             {
2459                 throw EntityUtil.NoEntryExistsForObject(entity);
2460             }
2461             return entry;
2462         }
2463
2464         internal EntityEntry GetEntityEntry(object entity)
2465         {
2466             Debug.Assert(entity != null, "entity is null");
2467             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
2468
2469             EntityEntry entry = FindEntityEntry(entity);
2470             if (entry == null)
2471             {
2472                 throw EntityUtil.NoEntryExistsForObject(entity);
2473             }
2474             return entry;
2475         }
2476
2477         /// <summary>
2478         /// Retrieve the corresponding ObjectStateEntry for the given object.
2479         /// </summary>
2480         /// <param name="entity"></param>
2481         /// <param name="entry"></param>
2482         /// <returns>true if the corresponding ObjectStateEntry was found</returns>
2483         public bool TryGetObjectStateEntry(object entity, out ObjectStateEntry entry)
2484         {
2485             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
2486             entry = null;
2487             EntityUtil.CheckArgumentNull(entity, "entity");
2488
2489             EntityKey entityKey = entity as EntityKey;
2490             if (entityKey != null)
2491             {
2492                 return TryGetObjectStateEntry(entityKey, out entry);
2493             }
2494             else
2495             {
2496                 entry = FindEntityEntry(entity);
2497             }
2498
2499             return entry != null;
2500         }
2501
2502         /// <summary>
2503         /// Retrieve the corresponding IEntityStateEntry for the given EntityKey.
2504         /// </summary>
2505         /// <returns>true if the corresponding IEntityStateEntry was found</returns>
2506         /// <exception cref="ArgumentNullException">if key is null</exception>
2507         bool IEntityStateManager.TryGetEntityStateEntry(EntityKey key, out IEntityStateEntry entry)
2508         {
2509             // Because the passed in IEntityStateEntry reference isn't necessarily an
2510             // ObjectStateEntry, we have to declare our own local copy, use it for the outparam of
2511             // TryGetObjectStateEntry, and then set it onto our outparam if we successfully find
2512             // something (at that point we know we can cast to IEntityStateEntry), but we just can't
2513             // cast in the other direction.
2514             ObjectStateEntry objectStateEntry;
2515             bool result = TryGetObjectStateEntry(key, out objectStateEntry);
2516             entry = (IEntityStateEntry)objectStateEntry;
2517             return result;
2518         }
2519
2520         /// <summary>
2521         /// Given a key that represents an entity on the dependent side of a FK, this method attempts to return the key of the
2522         /// entity on the principal side of the FK.  If the two entities both exist in the context, then the primary key of
2523         /// the principal entity is found and returned.  If the principal entity does not exist in the context, then a key
2524         /// for it is built up from the foreign key values contained in the dependent entity.
2525         /// </summary>
2526         /// <param name="dependentKey">The key of the dependent entity</param>
2527         /// <param name="principalRole">The role indicating the FK to navigate</param>
2528         /// <param name="principalKey">Set to the principal key or null on return</param>
2529         /// <returns>True if the principal key was found or built; false if it could not be found or built</returns>
2530         bool IEntityStateManager.TryGetReferenceKey(EntityKey dependentKey, AssociationEndMember principalRole, out EntityKey principalKey)
2531         {
2532             EntityEntry dependentEntry;
2533             if (!TryGetEntityEntry(dependentKey, out dependentEntry))
2534             {
2535                 principalKey = null;
2536                 return false;
2537             }
2538             return dependentEntry.TryGetReferenceKey(principalRole, out principalKey);
2539         }
2540
2541         /// <summary>
2542         /// Retrieve the corresponding ObjectStateEntry for the given EntityKey.
2543         /// </summary>
2544         /// <returns>true if the corresponding ObjectStateEntry was found</returns>
2545         /// <exception cref="ArgumentNullException">if key is null</exception>
2546         public bool TryGetObjectStateEntry(EntityKey key, out ObjectStateEntry entry)
2547         {
2548             bool result;
2549             EntityEntry entityEntry;
2550             result = this.TryGetEntityEntry(key, out entityEntry);
2551             entry = entityEntry;
2552             return result;
2553         }
2554
2555         internal bool TryGetEntityEntry(EntityKey key, out EntityEntry entry)
2556         {
2557             entry = null; // must set before checking for null key
2558             EntityUtil.CheckArgumentNull(key, "key");
2559             bool result;
2560             if (key.IsTemporary)
2561             {   // only temporary keys exist in the added state
2562                 result = ((null != _addedEntityStore) && _addedEntityStore.TryGetValue(key, out entry));
2563             }
2564             else
2565             {   // temporary keys do not exist in the unchanged, modified, deleted states.
2566                 result = (((null != _unchangedEntityStore) && _unchangedEntityStore.TryGetValue(key, out entry)) ||
2567                           ((null != _modifiedEntityStore) && _modifiedEntityStore.TryGetValue(key, out entry)) ||
2568                           ((null != _deletedEntityStore) && _deletedEntityStore.TryGetValue(key, out entry)));
2569             }
2570             Debug.Assert(result == (null != entry), "result and entry mismatch");
2571             return result;
2572         }
2573
2574         internal EntityEntry FindEntityEntry(EntityKey key)
2575         {
2576             EntityEntry entry = null;
2577             if (null != (object)key)
2578             {
2579                 TryGetEntityEntry(key, out entry);
2580             }
2581             return entry;
2582         }
2583
2584         /// <summary>
2585         /// Retrieve the corresponding EntityEntry for the given entity.
2586         /// Returns null if key is unavailable or passed entity is null.
2587         /// </summary>
2588         internal EntityEntry FindEntityEntry(object entity)
2589         {
2590             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
2591             Debug.Assert(!(entity is EntityKey), "Object is a EntityKey instead of raw entity.");
2592             EntityEntry entry = null;
2593             IEntityWithKey entityWithKey = entity as IEntityWithKey;
2594
2595             if (entityWithKey != null)
2596             {
2597                 EntityKey entityEntityKey = entityWithKey.EntityKey;
2598                 if (null != (object)entityEntityKey)
2599                 {
2600                     TryGetEntityEntry(entityEntityKey, out entry);
2601                 }
2602             }
2603             else
2604             {
2605                 TryGetEntryFromKeylessStore(entity, out entry);
2606             }
2607
2608             // If entity is detached, then entry.Entity won't have the same object reference.
2609             // This can happen if the same entity is loaded with, then without, tracking
2610             // SQL BU Defect Tracking 520058
2611             if (entry != null && !object.ReferenceEquals(entity, entry.Entity))
2612             {
2613                 entry = null;
2614             }
2615
2616             return entry;
2617         }
2618
2619         /// <summary>
2620         /// Gets a RelationshipManager for the given entity.  For entities that implement IEntityWithRelationships,
2621         /// the RelationshipManager is obtained through that interface.  For other types of entity, the RelationshipManager
2622         /// that is being tracked internally is returned.  This means that a RelationshipManager for an entity that
2623         /// does not implement IEntityWithRelationships can only be obtained if the entity is being tracked by the
2624         /// ObjectStateManager.
2625         /// Note that all code generated entities that inherit from EntityObject automatically implement IEntityWithRelationships.
2626         /// </summary>
2627         /// <param name="entity">The entity for which to return a RelationshipManager</param>
2628         /// <returns>The RelationshipManager</returns>
2629         /// <exception cref="InvalidOperationException">The entity does not implement IEntityWithRelationships and is not tracked by this ObjectStateManager</exception>
2630         public RelationshipManager GetRelationshipManager(object entity)
2631         {
2632             RelationshipManager rm;
2633             if (!TryGetRelationshipManager(entity, out rm))
2634             {
2635                 throw new InvalidOperationException(System.Data.Entity.Strings.ObjectStateManager_CannotGetRelationshipManagerForDetachedPocoEntity);
2636             }
2637             return rm;
2638         }
2639
2640         /// <summary>
2641         /// Gets a RelationshipManager for the given entity.  For entities that implement IEntityWithRelationships,
2642         /// the RelationshipManager is obtained through that interface.  For other types of entity, the RelationshipManager
2643         /// that is being tracked internally is returned.  This means that a RelationshipManager for an entity that
2644         /// does not implement IEntityWithRelationships can only be obtained if the entity is being tracked by the
2645         /// ObjectStateManager.
2646         /// Note that all code generated entities that inherit from EntityObject automatically implement IEntityWithRelationships.
2647         /// </summary>
2648         /// <param name="entity">The entity for which to return a RelationshipManager</param>
2649         /// <param name="relationshipManager">The RelationshipManager, or null if none was found</param>
2650         /// <returns>True if a RelationshipManager was found; false if The entity does not implement IEntityWithRelationships and is not tracked by this ObjectStateManager</returns>
2651         public bool TryGetRelationshipManager(object entity, out RelationshipManager relationshipManager)
2652         {
2653             EntityUtil.CheckArgumentNull(entity, "entity");
2654             IEntityWithRelationships withRelationships = entity as IEntityWithRelationships;
2655             if (withRelationships != null)
2656             {
2657                 relationshipManager = withRelationships.RelationshipManager;
2658                 if (relationshipManager == null)
2659                 {
2660                     throw EntityUtil.UnexpectedNullRelationshipManager();
2661                 }
2662                 if (relationshipManager.WrappedOwner.Entity != entity)
2663                 {
2664                     throw EntityUtil.InvalidRelationshipManagerOwner();
2665                 }
2666             }
2667             else
2668             {
2669                 IEntityWrapper wrappedEntity = EntityWrapperFactory.WrapEntityUsingStateManager(entity, this);
2670                 if (wrappedEntity.Context == null)
2671                 {
2672                     relationshipManager = null;
2673                     return false;
2674                 }
2675                 relationshipManager = wrappedEntity.RelationshipManager;
2676             }
2677             return true;
2678         }
2679
2680         internal void ChangeState(RelationshipEntry entry, EntityState oldState, EntityState newState)
2681         {
2682             if (newState == EntityState.Detached)
2683             {
2684                 // If we're transitioning to detached, completely remove all traces of the entry.
2685                 DeleteRelationshipFromLookup((RelationshipEntry)entry);
2686
2687                 // delay removal until RelationshipEnds is done
2688                 RemoveObjectStateEntryFromDictionary(entry, oldState);
2689
2690                 entry.Reset();
2691             }
2692             else
2693             {
2694                 RemoveObjectStateEntryFromDictionary(entry, oldState);
2695
2696                 // If we're transitioning to something other than detached, add the
2697                 // entry to the appropriate dictionary.
2698                 AddRelationshipEntryToDictionary(entry, newState);
2699             }
2700
2701             // do not fire event for relationship
2702         }
2703
2704         internal void ChangeState(EntityEntry entry, EntityState oldState, EntityState newState)
2705         {
2706             bool fireEvent = !entry.IsKeyEntry;
2707             if (newState == EntityState.Detached)
2708             {
2709                 // If we're transitioning to detached, completely remove all traces of the entry.
2710
2711                 // SQLBU 508278: Object State Manager should not allow "dangling" relationships to stay in the state manager.
2712                 // Remove potential dangling relationships
2713                 Debug.Assert((object)entry.EntityKey != null, "attached entry must have a key");
2714                 foreach (RelationshipEntry relationshipEntry in CopyOfRelationshipsByKey(entry.EntityKey))
2715                 {
2716                     ChangeState(relationshipEntry, relationshipEntry.State, EntityState.Detached);
2717                 }
2718
2719                 // delay removal until RelationshipEnds is done
2720                 RemoveObjectStateEntryFromDictionary(entry, oldState);
2721
2722                 IEntityWrapper wrappedEntity = entry.WrappedEntity; // we have to cache the entity before detaching it totally so we can fire event
2723                 entry.Reset();
2724                 // Prevent firing two events for removal from the context during rollback.
2725                 if (fireEvent && wrappedEntity.Entity != null && !TransactionManager.IsAttachTracking)
2726                 {
2727                     // first notify the view
2728                     OnEntityDeleted(CollectionChangeAction.Remove, wrappedEntity.Entity);
2729                     OnObjectStateManagerChanged(CollectionChangeAction.Remove, wrappedEntity.Entity);
2730                 }
2731             }
2732             else
2733             {
2734                 RemoveObjectStateEntryFromDictionary(entry, oldState);
2735
2736                 // If we're transitioning to something other than detached, add the
2737                 // entry to the appropriate dictionary.
2738                 AddEntityEntryToDictionary(entry, newState);
2739             }
2740
2741             if (newState == EntityState.Deleted)
2742             {
2743                 entry.RemoveFromForeignKeyIndex();
2744                 ForgetEntryWithConceptualNull(entry, resetAllKeys: true);
2745                 if (fireEvent)
2746                 {
2747                     // fire collectionChanged event only when an entity is being deleted (this includes deleting an added entity which becomes detached)
2748                     OnEntityDeleted(CollectionChangeAction.Remove, entry.Entity);
2749                     OnObjectStateManagerChanged(CollectionChangeAction.Remove, entry.Entity);
2750                 }
2751             }
2752         }
2753
2754         private void AddRelationshipEntryToDictionary(RelationshipEntry entry, EntityState state)
2755         {
2756             Debug.Assert(entry.IsRelationship, "expecting IsRelationship");
2757             Debug.Assert(null != entry.RelationshipWrapper, "null RelationshipWrapper");
2758
2759             Dictionary<RelationshipWrapper, RelationshipEntry> dictionaryToAdd = null;
2760             switch (state)
2761             {
2762                 case EntityState.Unchanged:
2763                     if (null == _unchangedRelationshipStore)
2764                     {
2765                         _unchangedRelationshipStore = new Dictionary<RelationshipWrapper, RelationshipEntry>();
2766                     }
2767                     dictionaryToAdd = _unchangedRelationshipStore;
2768                     break;
2769                 case EntityState.Added:
2770                     if (null == _addedRelationshipStore)
2771                     {
2772                         _addedRelationshipStore = new Dictionary<RelationshipWrapper, RelationshipEntry>();
2773                     }
2774                     dictionaryToAdd = _addedRelationshipStore;
2775                     break;
2776                 case EntityState.Deleted:
2777                     if (null == _deletedRelationshipStore)
2778                     {
2779                         _deletedRelationshipStore = new Dictionary<RelationshipWrapper, RelationshipEntry>();
2780                     }
2781                     dictionaryToAdd = _deletedRelationshipStore;
2782                     break;
2783                 default:
2784                     Debug.Assert(false, "Invalid state.");
2785                     break;
2786             }
2787             Debug.Assert(dictionaryToAdd != null, "Couldn't find the correct relationship dictionary based on entity state.");
2788             dictionaryToAdd.Add(entry.RelationshipWrapper, entry);
2789         }
2790
2791         private void AddEntityEntryToDictionary(EntityEntry entry, EntityState state)
2792         {
2793             Debug.Assert(null != (object)entry.EntityKey, "missing EntityKey");
2794
2795             if (entry.RequiresAnyChangeTracking)
2796             {
2797                 _detectChangesNeeded = true;
2798             }
2799
2800             Dictionary<EntityKey, EntityEntry> dictionaryToAdd = null;
2801             switch (state)
2802             {
2803                 case EntityState.Unchanged:
2804                     if (null == _unchangedEntityStore)
2805                     {
2806                         _unchangedEntityStore = new Dictionary<EntityKey, EntityEntry>();
2807                     }
2808                     dictionaryToAdd = _unchangedEntityStore;
2809                     Debug.Assert(!entry.EntityKey.IsTemporary, "adding temporary entity key into Unchanged state");
2810                     break;
2811                 case EntityState.Added:
2812                     if (null == _addedEntityStore)
2813                     {
2814                         _addedEntityStore = new Dictionary<EntityKey, EntityEntry>();
2815                     }
2816                     dictionaryToAdd = _addedEntityStore;
2817                     Debug.Assert(entry.EntityKey.IsTemporary, "adding non-temporary entity key into Added state");
2818                     break;
2819                 case EntityState.Deleted:
2820                     if (null == _deletedEntityStore)
2821                     {
2822                         _deletedEntityStore = new Dictionary<EntityKey, EntityEntry>();
2823                     }
2824                     dictionaryToAdd = _deletedEntityStore;
2825                     Debug.Assert(!entry.EntityKey.IsTemporary, "adding temporary entity key into Deleted state");
2826                     break;
2827                 case EntityState.Modified:
2828                     if (null == _modifiedEntityStore)
2829                     {
2830                         _modifiedEntityStore = new Dictionary<EntityKey, EntityEntry>();
2831                     }
2832                     dictionaryToAdd = _modifiedEntityStore;
2833                     Debug.Assert(!entry.EntityKey.IsTemporary, "adding temporary entity key into Modified state");
2834                     break;
2835                 default:
2836                     Debug.Assert(false, "Invalid state.");
2837                     break;
2838
2839
2840             }
2841             Debug.Assert(dictionaryToAdd != null, "Couldn't find the correct entity dictionary based on entity state.");
2842             dictionaryToAdd.Add(entry.EntityKey, entry);
2843             AddEntryToKeylessStore(entry);
2844
2845         }
2846
2847         private void AddEntryToKeylessStore(EntityEntry entry)
2848         {
2849             // Add an entry that doesn't implement IEntityWithKey to the keyless lookup.
2850             // It is used to lookup ObjectStateEntries when all we have is an entity reference.
2851             if (null != entry.Entity && !(entry.Entity is IEntityWithKey))
2852             {
2853                 if (null == _keylessEntityStore)
2854                 {
2855                     _keylessEntityStore = new Dictionary<object, EntityEntry>(new ObjectReferenceEqualityComparer());
2856                 }
2857                 if (!_keylessEntityStore.ContainsKey(entry.Entity))
2858                 {
2859                     _keylessEntityStore.Add(entry.Entity, entry);
2860                 }
2861             }
2862         }
2863
2864         /// <summary>
2865         /// Removes the given cache entry from the appropriate dictionary, based on
2866         /// the given state and whether or not the entry represents a relationship.
2867         /// </summary>
2868         private void RemoveObjectStateEntryFromDictionary(RelationshipEntry entry, EntityState state)
2869         {
2870             // Determine the appropriate dictionary from which to remove the entry.
2871             Dictionary<RelationshipWrapper, RelationshipEntry> dictionaryContainingEntry = null;
2872             switch (state)
2873             {
2874                 case EntityState.Unchanged:
2875                     dictionaryContainingEntry = _unchangedRelationshipStore;
2876                     break;
2877                 case EntityState.Added:
2878                     dictionaryContainingEntry = _addedRelationshipStore;
2879                     break;
2880                 case EntityState.Deleted:
2881                     dictionaryContainingEntry = _deletedRelationshipStore;
2882                     break;
2883                 default:
2884                     Debug.Assert(false, "Invalid state.");
2885                     break;
2886             }
2887             Debug.Assert(dictionaryContainingEntry != null, "Couldn't find the correct relationship dictionary based on entity state.");
2888
2889             bool result = dictionaryContainingEntry.Remove(entry.RelationshipWrapper);
2890             Debug.Assert(result, "The correct relationship dictionary based on entity state doesn't contain the entry.");
2891
2892             if (0 == dictionaryContainingEntry.Count)
2893             {   // reduce unused dictionary capacity
2894                 switch (state)
2895                 {
2896                     case EntityState.Unchanged:
2897                         _unchangedRelationshipStore = null;
2898                         break;
2899                     case EntityState.Added:
2900                         _addedRelationshipStore = null;
2901                         break;
2902                     case EntityState.Deleted:
2903                         _deletedRelationshipStore = null;
2904                         break;
2905                 }
2906             }
2907         }
2908
2909         /// <summary>
2910         /// Removes the given cache entry from the appropriate dictionary, based on
2911         /// the given state and whether or not the entry represents a relationship.
2912         /// </summary>
2913         private void RemoveObjectStateEntryFromDictionary(EntityEntry entry, EntityState state)
2914         {
2915             Dictionary<EntityKey, EntityEntry> dictionaryContainingEntry = null;
2916             switch (state)
2917             {
2918                 case EntityState.Unchanged:
2919                     dictionaryContainingEntry = _unchangedEntityStore;
2920                     break;
2921                 case EntityState.Added:
2922                     dictionaryContainingEntry = _addedEntityStore;
2923                     break;
2924                 case EntityState.Deleted:
2925                     dictionaryContainingEntry = _deletedEntityStore;
2926                     break;
2927                 case EntityState.Modified:
2928                     dictionaryContainingEntry = _modifiedEntityStore;
2929                     break;
2930                 default:
2931                     Debug.Assert(false, "Invalid state.");
2932                     break;
2933             }
2934             Debug.Assert(dictionaryContainingEntry != null, "Couldn't find the correct entity dictionary based on entity state.");
2935
2936             bool result = dictionaryContainingEntry.Remove(entry.EntityKey);
2937             Debug.Assert(result, "The correct entity dictionary based on entity state doesn't contain the entry.");
2938             RemoveEntryFromKeylessStore(entry.WrappedEntity);
2939
2940             if (0 == dictionaryContainingEntry.Count)
2941             {   // reduce unused dictionary capacity
2942                 switch (state)
2943                 {
2944                     case EntityState.Unchanged:
2945                         _unchangedEntityStore = null;
2946                         break;
2947                     case EntityState.Added:
2948                         _addedEntityStore = null;
2949                         break;
2950                     case EntityState.Deleted:
2951                         _deletedEntityStore = null;
2952                         break;
2953                     case EntityState.Modified:
2954                         _modifiedEntityStore = null;
2955                         break;
2956                 }
2957             }
2958         }
2959
2960         internal void RemoveEntryFromKeylessStore(IEntityWrapper wrappedEntity)
2961         {
2962             // Remove and entry from the store containing entities not implementing IEntityWithKey
2963             if (null != wrappedEntity && null != wrappedEntity.Entity && !(wrappedEntity.Entity is IEntityWithKey))
2964             {
2965                 _keylessEntityStore.Remove(wrappedEntity.Entity);
2966             }
2967         }
2968
2969         /// <summary>
2970         /// If a corresponding StateManagerTypeMetadata exists, it is returned.
2971         /// Otherwise, a StateManagerTypeMetadata is created and cached.
2972         /// </summary>
2973         internal StateManagerTypeMetadata GetOrAddStateManagerTypeMetadata(Type entityType, EntitySet entitySet)
2974         {
2975             Debug.Assert(entityType != null, "entityType cannot be null.");
2976             Debug.Assert(entitySet != null, "must have entitySet to correctly qualify Type");
2977
2978             StateManagerTypeMetadata typeMetadata;
2979             if (!_metadataMapping.TryGetValue(new EntitySetQualifiedType(entityType, entitySet), out typeMetadata))
2980             {
2981                 // GetMap doesn't have a mechanism to qualify identity with EntityContainerName
2982                 // This is unimportant until each EntityContainer can have its own ObjectTypeMapping.
2983                 typeMetadata = AddStateManagerTypeMetadata(entitySet, (ObjectTypeMapping)
2984                     MetadataWorkspace.GetMap(entityType.FullName, DataSpace.OSpace, DataSpace.OCSpace));
2985             }
2986             return typeMetadata;
2987         }
2988
2989         /// <summary>
2990         /// If a corresponding StateManagerTypeMetadata exists, it is returned.
2991         /// Otherwise, a StateManagerTypeMetadata is created and cached.
2992         /// </summary>
2993         internal StateManagerTypeMetadata GetOrAddStateManagerTypeMetadata(EdmType edmType)
2994         {
2995             Debug.Assert(edmType != null, "edmType cannot be null.");
2996             Debug.Assert(Helper.IsEntityType(edmType) ||
2997                          Helper.IsComplexType(edmType),
2998                          "only expecting ComplexType or EntityType");
2999
3000             StateManagerTypeMetadata typeMetadata;
3001             if (!_metadataStore.TryGetValue(edmType, out typeMetadata))
3002             {
3003                 typeMetadata = AddStateManagerTypeMetadata(edmType, (ObjectTypeMapping)
3004                     MetadataWorkspace.GetMap(edmType, DataSpace.OCSpace));
3005             }
3006             return typeMetadata;
3007         }
3008
3009         /// <summary>
3010         /// Creates an instance of StateManagerTypeMetadata from the given EdmType and ObjectMapping,
3011         /// and stores it in the metadata cache.  The new instance is returned.
3012         /// </summary>
3013         private StateManagerTypeMetadata AddStateManagerTypeMetadata(EntitySet entitySet, ObjectTypeMapping mapping)
3014         {
3015             Debug.Assert(null != entitySet, "null entitySet");
3016             Debug.Assert(null != mapping, "null mapping");
3017
3018             EdmType edmType = mapping.EdmType;
3019             Debug.Assert(Helper.IsEntityType(edmType) ||
3020                          Helper.IsComplexType(edmType),
3021                          "not Entity or complex type");
3022
3023             StateManagerTypeMetadata typeMetadata;
3024             if (!_metadataStore.TryGetValue(edmType, out typeMetadata))
3025             {
3026                 typeMetadata = new StateManagerTypeMetadata(edmType, mapping);
3027                 _metadataStore.Add(edmType, typeMetadata);
3028             }
3029
3030
3031             EntitySetQualifiedType entitySetQualifiedType = new EntitySetQualifiedType(mapping.ClrType.ClrType, entitySet);
3032             if (!_metadataMapping.ContainsKey(entitySetQualifiedType))
3033             {
3034                 _metadataMapping.Add(entitySetQualifiedType, typeMetadata);
3035             }
3036             else
3037             {
3038                 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Mapping_CannotMapCLRTypeMultipleTimes(typeMetadata.CdmMetadata.EdmType.FullName));
3039             }
3040             return typeMetadata;
3041         }
3042
3043         private StateManagerTypeMetadata AddStateManagerTypeMetadata(EdmType edmType, ObjectTypeMapping mapping)
3044         {
3045             Debug.Assert(null != edmType, "null EdmType");
3046             Debug.Assert(Helper.IsEntityType(edmType) ||
3047                          Helper.IsComplexType(edmType),
3048                          "not Entity or complex type");
3049
3050             StateManagerTypeMetadata typeMetadata = new StateManagerTypeMetadata(edmType, mapping);
3051             _metadataStore.Add(edmType, typeMetadata);
3052             return typeMetadata;
3053         }
3054
3055         /// <summary>
3056         /// Mark the ObjectStateManager as disposed
3057         /// </summary>
3058         internal void Dispose()
3059         {
3060             _isDisposed = true;
3061         }
3062
3063         internal bool IsDisposed
3064         {
3065             get { return _isDisposed; }
3066         }
3067
3068         /// <summary>
3069         /// For every tracked entity which doesn't implement IEntityWithChangeTracker detect changes in the entity's property values
3070         /// and marks appropriate ObjectStateEntry as Modified.
3071         /// For every tracked entity which doesn't implement IEntityWithRelationships detect changes in its relationships.
3072         /// 
3073         /// The method is used internally by ObjectContext.SaveChanges() but can be also used if user wants to detect changes 
3074         /// and have ObjectStateEntries in appropriate state before the SaveChanges() method is called.
3075         /// </summary>
3076         internal void DetectChanges()
3077         {
3078             var entries = this.GetEntityEntriesForDetectChanges();
3079             if (entries == null)
3080             {
3081                 return;
3082             }
3083
3084             if (this.TransactionManager.BeginDetectChanges())
3085             {
3086                 try
3087                 {
3088                     // Populate Transact-ionManager.DeletedRelationshipsByGraph and TransactionManager.AddedRelationshipsByGraph
3089                     this.DetectChangesInNavigationProperties(entries);
3090
3091                     // Populate TransactionManager.ChangedForeignKeys
3092                     this.DetectChangesInScalarAndComplexProperties(entries);
3093
3094                     // Populate TransactionManager.DeletedRelationshipsByForeignKey and TransactionManager.AddedRelationshipsByForeignKey
3095                     this.DetectChangesInForeignKeys(entries);
3096
3097                     // Detect conflicts between changes to FK and navigation properties
3098                     this.DetectConflicts(entries);
3099
3100                     // Update graph and FKs
3101                     this.TransactionManager.BeginAlignChanges();
3102                     this.AlignChangesInRelationships(entries);
3103                 }
3104                 finally
3105                 {
3106                     this.TransactionManager.EndAlignChanges();
3107                     this.TransactionManager.EndDetectChanges();
3108                 }
3109             }
3110         }
3111
3112         private void DetectConflicts(IList<EntityEntry> entries)
3113         {
3114             TransactionManager tm = this.TransactionManager;
3115             foreach (EntityEntry entry in entries)
3116             {
3117                 //NOTE: DetectChangesInNavigationProperties will have created two navigation changes
3118                 //      even if the user has only made a single change in there graph, this means we
3119                 //      only need to check for conflicts on the local end of the relationship.
3120
3121                 //Find all relationships being added for this entity
3122                 Dictionary<RelatedEnd, HashSet<IEntityWrapper>> addedRelationshipsByGraph;
3123                 tm.AddedRelationshipsByGraph.TryGetValue(entry.WrappedEntity, out addedRelationshipsByGraph);
3124                 Dictionary<RelatedEnd, HashSet<EntityKey>> addedRelationshipsByForeignKey;
3125                 tm.AddedRelationshipsByForeignKey.TryGetValue(entry.WrappedEntity, out addedRelationshipsByForeignKey);
3126
3127                 //Ensure new graph relationships do not involve a Deleted Entity
3128                 if (addedRelationshipsByGraph != null && addedRelationshipsByGraph.Count > 0)
3129                 {
3130                     if (entry.State == EntityState.Deleted)
3131                     {
3132                         throw EntityUtil.UnableToAddRelationshipWithDeletedEntity();
3133                     }
3134                 }
3135
3136                 //Check for conflicting FK changes and changes to PKs
3137                 if (addedRelationshipsByForeignKey != null)
3138                 {
3139                     foreach (var pair in addedRelationshipsByForeignKey)
3140                     {
3141                         //Ensure persisted dependents of identifying FK relationships are not being re-parented
3142                         if (entry.State == EntityState.Unchanged || entry.State == EntityState.Modified)
3143                         {
3144                             if (pair.Key.IsDependentEndOfReferentialConstraint(true) && pair.Value.Count > 0)
3145                             {
3146                                 throw EntityUtil.CannotChangeReferentialConstraintProperty();
3147                             }
3148                         }
3149
3150                         //Make sure each EntityReference only has one FK change 
3151                         //(It's possible to have more than one in an identifying 1:1/0..1 relationship 
3152                         // when two dependent FKs are set to match one principal)
3153                         EntityReference reference = pair.Key as EntityReference;
3154                         if (reference != null)
3155                         {
3156                             if (pair.Value.Count > 1)
3157                             {
3158                                 throw new InvalidOperationException(
3159                                     System.Data.Entity.Strings.ObjectStateManager_ConflictingChangesOfRelationshipDetected(
3160                                         pair.Key.RelationshipNavigation.To,
3161                                         pair.Key.RelationshipNavigation.RelationshipName));
3162                             }
3163                         }
3164                     }
3165                 }
3166
3167                 //Check for conflicting reference changes and changes that will change a PK
3168                 if (addedRelationshipsByGraph != null)
3169                 {
3170                     // Retrieve key values from related entities
3171                     Dictionary<string, KeyValuePair<object, IntBox>> properties = new Dictionary<string, KeyValuePair<object, IntBox>>();
3172
3173                     foreach (var pair in addedRelationshipsByGraph)
3174                     {
3175                         //Ensure persisted dependents of identifying FK relationships are not being re-parented
3176                         if (pair.Key.IsForeignKey && (entry.State == EntityState.Unchanged || entry.State == EntityState.Modified))
3177                         {
3178                             //Any reference change is invalid because it is not possible to have a persisted 
3179                             //principal that matches the dependents key without the reference already being set
3180                             if (pair.Key.IsDependentEndOfReferentialConstraint(true) && pair.Value.Count > 0)
3181                             {
3182                                 throw EntityUtil.CannotChangeReferentialConstraintProperty();
3183                             }
3184                         }
3185
3186                         //Check that each EntityReference only has one reference change
3187                         //AND that the change agrees with the FK change if present
3188                         EntityReference reference = pair.Key as EntityReference;
3189                         if (reference != null)
3190                         {
3191                             if (pair.Value.Count > 1)
3192                             {
3193                                 throw new InvalidOperationException(
3194                                     System.Data.Entity.Strings.ObjectStateManager_ConflictingChangesOfRelationshipDetected(
3195                                         pair.Key.RelationshipNavigation.To,
3196                                         pair.Key.RelationshipNavigation.RelationshipName));
3197                             }
3198                             else if (pair.Value.Count == 1)
3199                             {
3200                                 //We know there is a max of one FK change as we checked this already
3201                                 IEntityWrapper addedEntity = pair.Value.First();
3202
3203                                 //See if there is also a new FK for this RelatedEnd
3204                                 HashSet<EntityKey> newFks = null;
3205                                 if (addedRelationshipsByForeignKey != null)
3206                                 {
3207                                     addedRelationshipsByForeignKey.TryGetValue(pair.Key, out newFks);
3208                                 }
3209                                 else
3210                                 {
3211                                     // Try the principal key dictionary to see if there is a conflict on the principal side
3212                                     Dictionary<RelatedEnd, HashSet<EntityKey>> addedRelationshipsByPrincipalKey;
3213                                     if (tm.AddedRelationshipsByPrincipalKey.TryGetValue(entry.WrappedEntity, out addedRelationshipsByPrincipalKey))
3214                                     {
3215                                         addedRelationshipsByPrincipalKey.TryGetValue(pair.Key, out newFks);
3216                                     }
3217                                 }
3218
3219                                 if (newFks != null && newFks.Count > 0)
3220                                 {
3221                                     //Make sure the FK change is consistent with the Reference change
3222                                     //The following call sometimes creates permanent key of Added entity
3223                                     EntityKey addedKey = GetPermanentKey(entry.WrappedEntity, reference, addedEntity);
3224
3225                                     if (addedKey != newFks.First())
3226                                     {
3227                                         throw new InvalidOperationException(System.Data.Entity.Strings.ObjectStateManager_ConflictingChangesOfRelationshipDetected(
3228                                             reference.RelationshipNavigation.To,
3229                                             reference.RelationshipNavigation.RelationshipName));
3230                                     }
3231                                 }
3232                                 else
3233                                 {
3234                                     //If there is no added FK relationship but there is a deleted one then it means
3235                                     //the FK has been nulled and this will always conflict with an added reference
3236                                     Dictionary<RelatedEnd, HashSet<EntityKey>> deletedRelationshipsByForeignKey;
3237                                     if (tm.DeletedRelationshipsByForeignKey.TryGetValue(entry.WrappedEntity, out deletedRelationshipsByForeignKey))
3238                                     {
3239                                         HashSet<EntityKey> removedKeys;
3240                                         if (deletedRelationshipsByForeignKey.TryGetValue(pair.Key, out removedKeys))
3241                                         {
3242                                             if (removedKeys.Count > 0)
3243                                             {
3244                                                 throw new InvalidOperationException(System.Data.Entity.Strings.ObjectStateManager_ConflictingChangesOfRelationshipDetected(
3245                                                     reference.RelationshipNavigation.To,
3246                                                     reference.RelationshipNavigation.RelationshipName));
3247                                             }
3248                                         }
3249                                     }
3250                                 }
3251
3252                                 // For each change to the graph, validate that the entity will not have conflicting 
3253                                 //   RI constrained property values
3254                                 // The related entity is detached or added, these are valid cases 
3255                                 //   so do not consider their changes in conflict
3256                                 EntityEntry relatedEntry = FindEntityEntry(addedEntity.Entity);
3257                                 if (relatedEntry != null &&
3258                                     (relatedEntry.State == EntityState.Unchanged ||
3259                                      relatedEntry.State == EntityState.Modified))
3260                                 {
3261                                     Dictionary<string, KeyValuePair<object, IntBox>> retrievedProperties = new Dictionary<string, KeyValuePair<object, IntBox>>();
3262                                     relatedEntry.GetOtherKeyProperties(retrievedProperties);
3263                                     // Merge retrievedProperties into the main list of properties
3264                                     foreach (ReferentialConstraint constraint in ((AssociationType)reference.RelationMetadata).ReferentialConstraints)
3265                                     {
3266                                         if (constraint.ToRole == reference.FromEndProperty)
3267                                         {
3268                                             for (int i = 0; i < constraint.FromProperties.Count; ++i)
3269                                             {
3270                                                 EntityEntry.AddOrIncreaseCounter(
3271                                                         properties,
3272                                                         constraint.ToProperties[i].Name,
3273                                                         retrievedProperties[constraint.FromProperties[i].Name].Key);
3274                                             }
3275                                             break;
3276                                         }
3277                                     }
3278
3279                                 }
3280                             }
3281                         }
3282                     }
3283                 }
3284             }
3285         }
3286
3287         internal EntityKey GetPermanentKey(IEntityWrapper entityFrom, RelatedEnd relatedEndFrom, IEntityWrapper entityTo)
3288         {
3289             EntityKey entityKey = null;
3290             if (entityTo.ObjectStateEntry != null)
3291             {
3292                 entityKey = entityTo.ObjectStateEntry.EntityKey;
3293             }
3294             if (entityKey == null || entityKey.IsTemporary)
3295             {
3296                 entityKey = this.CreateEntityKey(this.GetEntitySetOfOtherEnd(entityFrom, relatedEndFrom), entityTo.Entity);
3297             }
3298             return entityKey;
3299         }
3300
3301
3302         private EntitySet GetEntitySetOfOtherEnd(IEntityWrapper entity, RelatedEnd relatedEnd)
3303         {
3304             AssociationSet associationSet = (AssociationSet)relatedEnd.RelationshipSet;
3305
3306             EntitySet entitySet = associationSet.AssociationSetEnds[0].EntitySet;
3307             if (entitySet.Name != entity.EntityKey.EntitySetName) // 
3308             {
3309                 return entitySet;
3310             }
3311             else
3312             {
3313                 return associationSet.AssociationSetEnds[1].EntitySet;
3314             }
3315         }
3316
3317         private void DetectChangesInForeignKeys(IList<EntityEntry> entries)
3318         {
3319             foreach (var entry in entries)
3320             {
3321                 if (entry.State == EntityState.Added || entry.State == EntityState.Modified)
3322                 {
3323                     entry.DetectChangesInForeignKeys();
3324                 }
3325             }
3326         }
3327
3328         private void AlignChangesInRelationships(IList<EntityEntry> entries)
3329         {
3330             PerformDelete(entries);
3331             PerformAdd(entries);
3332         }
3333
3334         private void PerformAdd(IList<EntityEntry> entries)
3335         {
3336             TransactionManager tm = this.TransactionManager;
3337
3338             foreach (EntityEntry entry in entries)
3339             {
3340                 if (entry.State != EntityState.Detached &&
3341                     !entry.IsKeyEntry) // Still need to check this here because entries may have been demoted
3342                 {
3343                     foreach (RelatedEnd relatedEnd in entry.WrappedEntity.RelationshipManager.Relationships)
3344                     {
3345                         // find EntityKey of objects added to relatedEnd by changes of FKs
3346
3347                         HashSet<EntityKey> entityKeysOfAddedObjects = null;
3348
3349                         Dictionary<RelatedEnd, HashSet<EntityKey>> addedRelationshipsByForeignKey;
3350                         if (relatedEnd is EntityReference &&
3351                             tm.AddedRelationshipsByForeignKey.TryGetValue(entry.WrappedEntity, out addedRelationshipsByForeignKey))
3352                         {
3353                             addedRelationshipsByForeignKey.TryGetValue(relatedEnd, out entityKeysOfAddedObjects);
3354                         }
3355
3356
3357                         // find IEntityWrappers of objects added to relatedEnd by changes to navigation property
3358
3359                         Dictionary<RelatedEnd, HashSet<IEntityWrapper>> addedRelationshipsByGraph;
3360                         HashSet<IEntityWrapper> entitiesToAdd = null;
3361                         if (tm.AddedRelationshipsByGraph.TryGetValue(entry.WrappedEntity, out addedRelationshipsByGraph))
3362                         {
3363                             addedRelationshipsByGraph.TryGetValue(relatedEnd, out entitiesToAdd);
3364                         }
3365
3366
3367                         // merge the 2 sets into one (destroys entitiesToAdd)
3368
3369                         // Perform Add of FK or FK + Reference changes
3370                         if (entityKeysOfAddedObjects != null)
3371                         {
3372                             EntityEntry relatedEntry;
3373
3374                             foreach (EntityKey entityKeyOfAddedObjects in entityKeysOfAddedObjects)
3375                             {
3376                                 // we are interested only in tracked non-Added entities
3377                                 if (this.TryGetEntityEntry(entityKeyOfAddedObjects, out relatedEntry) &&
3378                                     relatedEntry.WrappedEntity.Entity != null)
3379                                 {
3380                                     entitiesToAdd = entitiesToAdd != null ? entitiesToAdd : new HashSet<IEntityWrapper>();
3381                                     // if the change comes only from the FK and the FK is to a deleted entity
3382                                     // then we do not do fixup to align to that entity so do not add those
3383                                     // implementation note: we do not need to check for contains because if it's there we don't need to add it
3384                                     if (relatedEntry.State != EntityState.Deleted)
3385                                     {
3386                                         // Remove it from the list of entities to add by reference because it will be added now
3387                                         entitiesToAdd.Remove(relatedEntry.WrappedEntity);
3388
3389                                         PerformAdd(entry.WrappedEntity, relatedEnd, relatedEntry.WrappedEntity, true);
3390                                     }
3391                                 }
3392                                 else
3393                                 {
3394                                     // Need to update the CFK and dangling FK references even if there is no related entity
3395                                     EntityReference reference = relatedEnd as EntityReference;
3396                                     Debug.Assert(reference != null);
3397                                     entry.FixupEntityReferenceByForeignKey(reference);
3398                                 }
3399                             }
3400                         }
3401
3402
3403                         // Perform Add for Reference changes
3404                         if (entitiesToAdd != null)
3405                         {
3406                             foreach (IEntityWrapper entityToAdd in entitiesToAdd)
3407                             {
3408                                 PerformAdd(entry.WrappedEntity, relatedEnd, entityToAdd, false);
3409                             }
3410                         }
3411                     }
3412                 }
3413             }
3414         }
3415
3416         private void PerformAdd(IEntityWrapper wrappedOwner, RelatedEnd relatedEnd, IEntityWrapper entityToAdd, bool isForeignKeyChange)
3417         {
3418             Debug.Assert(wrappedOwner == relatedEnd.WrappedOwner, "entry.WrappedEntity is not the same as relatedEnd.WrappedOwner?");
3419
3420             relatedEnd.ValidateStateForAdd(relatedEnd.WrappedOwner);
3421             relatedEnd.ValidateStateForAdd(entityToAdd);
3422
3423             // We need to determine if adding entityToAdd is going to cause reparenting
3424             // if relatedEnd is a principal then
3425             //   Get the target relatedEnd on entityToAdd to check if we are in this situation
3426             // if relatedEnd is a dependent then
3427             //   Check 
3428             if (relatedEnd.IsPrincipalEndOfReferentialConstraint())
3429             {
3430                 EntityReference targetReference = relatedEnd.GetOtherEndOfRelationship(entityToAdd) as EntityReference;
3431                 if (targetReference != null && IsReparentingReference(entityToAdd, targetReference))
3432                 {
3433                     TransactionManager.EntityBeingReparented = targetReference.GetDependentEndOfReferentialConstraint(targetReference.ReferenceValue.Entity);
3434                 }
3435             }
3436             else if (relatedEnd.IsDependentEndOfReferentialConstraint(checkIdentifying: false))
3437             {
3438                 EntityReference reference = relatedEnd as EntityReference;
3439                 if (reference != null && IsReparentingReference(wrappedOwner, reference))
3440                 {
3441                     TransactionManager.EntityBeingReparented = reference.GetDependentEndOfReferentialConstraint(reference.ReferenceValue.Entity);
3442                 }
3443             }
3444             try
3445             {
3446                 relatedEnd.Add(entityToAdd,
3447                     applyConstraints: false,
3448                     addRelationshipAsUnchanged: false,
3449                     relationshipAlreadyExists: false,
3450                     allowModifyingOtherEndOfRelationship: true,
3451                     forceForeignKeyChanges: !isForeignKeyChange);
3452             }
3453             finally
3454             {
3455                 TransactionManager.EntityBeingReparented = null;
3456             }
3457         }
3458
3459         private void PerformDelete(IList<EntityEntry> entries)
3460         {
3461             TransactionManager tm = this.TransactionManager;
3462
3463             foreach (EntityEntry entry in entries)
3464             {
3465                 if (entry.State != EntityState.Detached &&
3466                     entry.State != EntityState.Deleted &&
3467                     !entry.IsKeyEntry) // Still need to check this here because entries may have been demoted
3468                 {
3469                     foreach (RelatedEnd relatedEnd in entry.WrappedEntity.RelationshipManager.Relationships)
3470                     {
3471                         // find EntityKey of objects deleted from relatedEnd by changes of FKs
3472
3473                         HashSet<EntityKey> entityKeysOfDeletedObjects = null;
3474
3475                         Dictionary<RelatedEnd, HashSet<EntityKey>> deletedRelationshipsByForeignKey;
3476                         if (relatedEnd is EntityReference &&
3477                             tm.DeletedRelationshipsByForeignKey.TryGetValue(entry.WrappedEntity, out deletedRelationshipsByForeignKey))
3478                         {
3479                             deletedRelationshipsByForeignKey.TryGetValue(relatedEnd as EntityReference, out entityKeysOfDeletedObjects);
3480                         }
3481
3482                         // find IEntityWrappers of objects deleted from relatedEnd by changes to navigation property
3483
3484                         Dictionary<RelatedEnd, HashSet<IEntityWrapper>> deletedRelationshipsByGraph;
3485                         HashSet<IEntityWrapper> entitiesToDelete = null;
3486                         if (tm.DeletedRelationshipsByGraph.TryGetValue(entry.WrappedEntity, out deletedRelationshipsByGraph))
3487                         {
3488                             deletedRelationshipsByGraph.TryGetValue(relatedEnd, out entitiesToDelete);
3489                         }
3490
3491                         // Perform the deletes:
3492                         // 1. FK only OR combined FK/Ref changes (same change to both FK and reference)
3493                         if (entityKeysOfDeletedObjects != null)
3494                         {
3495                             foreach (EntityKey key in entityKeysOfDeletedObjects)
3496                             {
3497                                 EntityEntry relatedEntry;
3498                                 IEntityWrapper relatedEntity = null;
3499                                 EntityReference reference = relatedEnd as EntityReference;
3500                                 if (this.TryGetEntityEntry(key, out relatedEntry) &&
3501                                     relatedEntry.WrappedEntity.Entity != null)
3502                                 {
3503                                     relatedEntity = relatedEntry.WrappedEntity;
3504                                 }
3505                                 else
3506                                 {
3507                                     // The relatedEntity may be added, and we only have a permanent key 
3508                                     //  so look at the permanent key of the reference to decide
3509                                     if (reference != null &&
3510                                         reference.ReferenceValue != NullEntityWrapper.NullWrapper &&
3511                                         reference.ReferenceValue.EntityKey.IsTemporary &&
3512                                         this.TryGetEntityEntry(reference.ReferenceValue.EntityKey, out relatedEntry) &&
3513                                         relatedEntry.WrappedEntity.Entity != null)
3514                                     {
3515                                         EntityKey permanentRelatedKey = new EntityKey((EntitySet)relatedEntry.EntitySet, (IExtendedDataRecord)relatedEntry.CurrentValues);
3516                                         if (key == permanentRelatedKey)
3517                                         {
3518                                             relatedEntity = relatedEntry.WrappedEntity;
3519                                         }
3520                                     }
3521                                 }
3522
3523                                 if (relatedEntity != null)
3524                                 {
3525                                     entitiesToDelete = entitiesToDelete != null ? entitiesToDelete : new HashSet<IEntityWrapper>();
3526                                     // if the reference also changed, we will remove that now
3527                                     // if only the FK changed, it will not be in the list entitiesToDelete and 
3528                                     //  so we should preserve the FK value
3529                                     // if the reference is being set to null, (was a delete, but not an add) 
3530                                     //  then we need to preserve the FK values regardless
3531                                     bool preserveForeignKey = ShouldPreserveForeignKeyForDependent(entry.WrappedEntity, relatedEnd, relatedEntity, entitiesToDelete);
3532                                     // No need to also do a graph remove of the same value
3533                                     entitiesToDelete.Remove(relatedEntity);
3534                                     if (reference != null && IsReparentingReference(entry.WrappedEntity, reference))
3535                                     {
3536                                         TransactionManager.EntityBeingReparented = reference.GetDependentEndOfReferentialConstraint(reference.ReferenceValue.Entity);
3537                                     }
3538                                     try
3539                                     {
3540                                         relatedEnd.Remove(relatedEntity, preserveForeignKey);
3541                                     }
3542                                     finally
3543                                     {
3544                                         TransactionManager.EntityBeingReparented = null;
3545                                     }
3546                                     // stop trying to remove something, if the owner was detached or deleted because of RIC/cascade delete
3547                                     if (entry.State == EntityState.Detached || entry.State == EntityState.Deleted || entry.IsKeyEntry)
3548                                     {
3549                                         break;
3550                                     }
3551                                 }
3552                                 if (reference != null &&
3553                                     reference.IsForeignKey &&
3554                                     reference.IsDependentEndOfReferentialConstraint(checkIdentifying: false))
3555                                 {
3556                                     // Ensure that the cached FK value on the reference is in sync because it is possible that we
3557                                     // didn't take any actions above that would cause this to be set.
3558                                     reference.SetCachedForeignKey(ForeignKeyFactory.CreateKeyFromForeignKeyValues(entry, reference), entry);
3559                                 }
3560                             }
3561                         }
3562
3563                         // 2. Changes to the reference only
3564                         if (entitiesToDelete != null)
3565                         {
3566                             foreach (IEntityWrapper entityToDelete in entitiesToDelete)
3567                             {
3568                                 bool preserveForeignKey = ShouldPreserveForeignKeyForPrincipal(entry.WrappedEntity, relatedEnd, entityToDelete, entitiesToDelete);
3569                                 EntityReference reference = relatedEnd as EntityReference;
3570                                 if (reference != null && IsReparentingReference(entry.WrappedEntity, reference))
3571                                 {
3572                                     TransactionManager.EntityBeingReparented = reference.GetDependentEndOfReferentialConstraint(reference.ReferenceValue.Entity);
3573                                 }
3574                                 try
3575                                 {
3576                                     relatedEnd.Remove(entityToDelete, preserveForeignKey);
3577                                 }
3578                                 finally
3579                                 {
3580                                     TransactionManager.EntityBeingReparented = null;
3581                                 }
3582
3583                                 // stop trying to remove something, if the owner was detached or deleted because of RIC/cascade delete
3584                                 if (entry.State == EntityState.Detached || entry.State == EntityState.Deleted || entry.IsKeyEntry)
3585                                 {
3586                                     break;
3587                                 }
3588                             }
3589                         }
3590
3591                         // skip the remaining relatedEnds if the owner was detached or deleted because of RIC/cascade delete
3592                         if (entry.State == EntityState.Detached || entry.State == EntityState.Deleted || entry.IsKeyEntry)
3593                         {
3594                             break;
3595                         }
3596                     }
3597                 }
3598             }
3599         }
3600
3601         private bool ShouldPreserveForeignKeyForPrincipal(IEntityWrapper entity, RelatedEnd relatedEnd, IEntityWrapper relatedEntity,
3602             HashSet<IEntityWrapper> entitiesToDelete)
3603         {
3604             bool preserveForeignKey = false;
3605             if (relatedEnd.IsForeignKey)
3606             {
3607                 RelatedEnd otherEnd = relatedEnd.GetOtherEndOfRelationship(relatedEntity);
3608                 if (otherEnd.IsDependentEndOfReferentialConstraint(false))
3609                 {
3610                     // Check the changes being applied to the dependent end
3611                     HashSet<EntityKey> entityKeysOfDeletedObjects = null;
3612                     Dictionary<RelatedEnd, HashSet<EntityKey>> deletedRelationshipsByForeignKey;
3613                     Dictionary<RelatedEnd, HashSet<IEntityWrapper>> deletedRelationshipsByGraph;
3614                     // There must be a foreign key and graph change on the dependent side to know if we need to preserve the FK
3615                     if (TransactionManager.DeletedRelationshipsByForeignKey.TryGetValue(relatedEntity, out deletedRelationshipsByForeignKey) &&
3616                         deletedRelationshipsByForeignKey.TryGetValue(otherEnd, out entityKeysOfDeletedObjects) &&
3617                         entityKeysOfDeletedObjects.Count > 0 &&
3618                         TransactionManager.DeletedRelationshipsByGraph.TryGetValue(relatedEntity, out deletedRelationshipsByGraph) &&
3619                         deletedRelationshipsByGraph.TryGetValue(otherEnd, out entitiesToDelete))
3620                     {
3621                         preserveForeignKey = ShouldPreserveForeignKeyForDependent(relatedEntity, otherEnd, entity, entitiesToDelete);
3622                     }
3623                 }
3624             }
3625             return preserveForeignKey;
3626         }
3627
3628         private bool ShouldPreserveForeignKeyForDependent(IEntityWrapper entity, RelatedEnd relatedEnd, IEntityWrapper relatedEntity,
3629             HashSet<IEntityWrapper> entitiesToDelete)
3630         {
3631             bool hasReferenceRemove = entitiesToDelete.Contains(relatedEntity);
3632             return (!hasReferenceRemove ||
3633                      hasReferenceRemove && !HasAddedReference(entity, relatedEnd as EntityReference));
3634         }
3635
3636         private bool HasAddedReference(IEntityWrapper wrappedOwner, EntityReference reference)
3637         {
3638             Dictionary<RelatedEnd, HashSet<IEntityWrapper>> addedRelationshipsByGraph;
3639             HashSet<IEntityWrapper> entitiesToAdd = null;
3640             if (reference != null &&
3641                 TransactionManager.AddedRelationshipsByGraph.TryGetValue(wrappedOwner, out addedRelationshipsByGraph) &&
3642                 addedRelationshipsByGraph.TryGetValue(reference, out entitiesToAdd) &&
3643                 entitiesToAdd.Count > 0)
3644             {
3645                 return true;
3646             }
3647             return false;
3648         }
3649
3650         private bool IsReparentingReference(IEntityWrapper wrappedEntity, EntityReference reference)
3651         {
3652             TransactionManager tm = this.TransactionManager;
3653             if (reference.IsPrincipalEndOfReferentialConstraint())
3654             {
3655                 // need to find the dependent and make sure that it is being reparented
3656                 wrappedEntity = reference.ReferenceValue;
3657                 reference = wrappedEntity.Entity == null ?
3658                     null :
3659                     reference.GetOtherEndOfRelationship(wrappedEntity) as EntityReference;
3660             }
3661
3662             if (wrappedEntity.Entity != null && reference != null)
3663             {
3664                 HashSet<EntityKey> entityKeysOfAddedObjects = null;
3665                 Dictionary<RelatedEnd, HashSet<EntityKey>> addedRelationshipsByForeignKey;
3666                 if (tm.AddedRelationshipsByForeignKey.TryGetValue(wrappedEntity, out addedRelationshipsByForeignKey) &&
3667                     addedRelationshipsByForeignKey.TryGetValue(reference, out entityKeysOfAddedObjects) &&
3668                     entityKeysOfAddedObjects.Count > 0)
3669                 {
3670                     return true;
3671                 }
3672
3673                 Dictionary<RelatedEnd, HashSet<IEntityWrapper>> addedRelationshipsByGraph;
3674                 HashSet<IEntityWrapper> entitiesToAdd = null;
3675                 if (tm.AddedRelationshipsByGraph.TryGetValue(wrappedEntity, out addedRelationshipsByGraph) &&
3676                     addedRelationshipsByGraph.TryGetValue(reference, out entitiesToAdd) &&
3677                     entitiesToAdd.Count > 0)
3678                 {
3679                     return true;
3680                 }
3681             }
3682             return false;
3683         }
3684
3685         private void DetectChangesInNavigationProperties(IList<EntityEntry> entries)
3686         {
3687             // Detect changes in navigation properties
3688             // (populates this.TransactionManager.DeletedRelationships and this.TransactionManager.AddedRelationships)
3689             foreach (var entry in entries)
3690             {
3691                 Debug.Assert(!entry.IsKeyEntry, "List should be filtered before it gets to this method.");
3692                 if (entry.WrappedEntity.RequiresRelationshipChangeTracking)
3693                 {
3694                     entry.DetectChangesInRelationshipsOfSingleEntity();
3695                 }
3696             }
3697         }
3698
3699         private void DetectChangesInScalarAndComplexProperties(IList<EntityEntry> entries)
3700         {
3701             foreach (var entry in entries)
3702             {
3703                 Debug.Assert(!entry.IsKeyEntry, "List should be filtered before it gets to this method.");
3704
3705                 if (entry.State != EntityState.Added)
3706                 {
3707                     if (entry.RequiresScalarChangeTracking || entry.RequiresComplexChangeTracking)
3708                     {
3709                         entry.DetectChangesInProperties(!entry.RequiresScalarChangeTracking);
3710                     }
3711                 }
3712             }
3713         }
3714
3715         internal EntityKey CreateEntityKey(EntitySet entitySet, object entity)
3716         {
3717             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
3718             Debug.Assert(entitySet != null, "null entitySet");
3719             Debug.Assert(entity != null, "null entity");
3720
3721             // Creates an EntityKey based on the values in the entity and the given EntitySet
3722             ReadOnlyMetadataCollection<EdmMember> keyMembers = entitySet.ElementType.KeyMembers;
3723             StateManagerTypeMetadata typeMetadata = this.GetOrAddStateManagerTypeMetadata(EntityUtil.GetEntityIdentityType(entity.GetType()), entitySet);
3724             object[] keyValues = new object[keyMembers.Count];
3725
3726             for (int i = 0; i < keyMembers.Count; ++i)
3727             {
3728                 string keyName = keyMembers[i].Name;
3729                 int ordinal = typeMetadata.GetOrdinalforCLayerMemberName(keyName);
3730                 if (ordinal < 0)
3731                 {
3732                     throw EntityUtil.EntityTypeDoesNotMatchEntitySet(entity.GetType().FullName, entitySet.Name, "entity");
3733                 }
3734
3735                 keyValues[i] = typeMetadata.Member(ordinal).GetValue(entity);
3736                 if (keyValues[i] == null)
3737                 {
3738                     throw EntityUtil.NullKeyValue(keyName, entitySet.ElementType.Name);
3739                 }
3740             }
3741
3742             if (keyValues.Length == 1)
3743             {
3744                 return new EntityKey(entitySet, keyValues[0]);
3745             }
3746             else
3747             {
3748                 return new EntityKey(entitySet, keyValues);
3749             }
3750         }
3751
3752         /// <summary>
3753         /// Flag that is set when we are processing an FK setter for a full proxy.
3754         /// This is used to determine whether or not we will attempt to call out into FK
3755         /// setters and null references during fixup.
3756         /// The value of this property is either null if the code is not executing an
3757         /// FK setter, or points to the entity on which the FK setter has been called.
3758         /// </summary>
3759         internal object EntityInvokingFKSetter
3760         {
3761             get;
3762             set;
3763         }
3764     }
3765
3766     internal sealed class ObjectReferenceEqualityComparer : IEqualityComparer<object>
3767     {
3768         bool IEqualityComparer<object>.Equals(object x, object y)
3769         {
3770             return (Object.ReferenceEquals(x, y));
3771         }
3772
3773         int IEqualityComparer<object>.GetHashCode(object obj)
3774         {
3775             return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj);
3776         }
3777     }
3778 }