Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Objects / DataClasses / RelatedEnd.cs
1 //---------------------------------------------------------------------
2 // <copyright file="RelatedEnd.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.DataClasses
11 {
12     using System.Collections;
13     using System.Collections.Generic;
14     using System.ComponentModel;
15     using System.Data.Common.Utils;
16     using System.Data.Metadata.Edm;
17     using System.Data.Objects.Internal;
18     using System.Diagnostics;
19     using System.Linq;
20     using System.Runtime.Serialization;
21     using System.Text;
22
23     /// <summary>
24     /// Base class for EntityCollection and EntityReference
25     /// </summary>
26
27     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
28     [DataContract]
29     [Serializable]
30     public abstract class RelatedEnd : IRelatedEnd
31     {
32
33         //-----------------
34         // Internal Constructors
35         //-----------------
36
37         /// <summary>
38         /// The default constructor is required for some serialization scenarios with EntityReference.
39         /// </summary>
40         internal RelatedEnd()
41         {
42             _wrappedOwner = EntityWrapperFactory.NullWrapper;
43         }
44
45         internal RelatedEnd(IEntityWrapper wrappedOwner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer)
46         {
47             EntityUtil.CheckArgumentNull(wrappedOwner, "wrappedOwner");
48             EntityUtil.CheckArgumentNull(wrappedOwner.Entity, "wrappedOwner.Entity");
49             EntityUtil.CheckArgumentNull(navigation, "navigation");
50             EntityUtil.CheckArgumentNull(relationshipFixer, "fixer");
51
52             InitializeRelatedEnd(wrappedOwner, navigation, relationshipFixer);
53         }
54
55         // ------
56         // Fields
57         // ------
58         private const string _entityKeyParamName = "EntityKeyValue";
59
60         // The following fields are serialized.  Adding or removing a serialized field is considered
61         // a breaking change.  This includes changing the field type or field name of existing
62         // serialized fields. If you need to make this kind of change, it may be possible, but it
63         // will require some custom serialization/deserialization code.
64         // These fields should not be changed once they have been initialized with non-null values, but they can't be read-only because there
65         // are serialization scenarios where they have to be set after construction
66
67         /// <summary>
68         /// Note that this field should no longer be used directly.  Instead, use the _wrappedOwner
69         /// field.  This field is retained only for compatibility with the serialization format introduced in v1.
70         /// </summary>
71         [Obsolete()]
72         private IEntityWithRelationships _owner;
73
74         private RelationshipNavigation _navigation;
75         private IRelationshipFixer _relationshipFixer;
76
77         internal bool _isLoaded;
78
79         // The fields in this group are set only when attached to a context, so we don't need to serialize.
80         [NonSerialized]
81         private RelationshipSet _relationshipSet;
82         [NonSerialized]
83         private ObjectContext _context;
84         [NonSerialized]
85         private bool _usingNoTracking;
86         [NonSerialized]
87         private RelationshipType _relationMetadata;
88         [NonSerialized]
89         private RelationshipEndMember _fromEndProperty; //owner end property
90         [NonSerialized]
91         private RelationshipEndMember _toEndProperty;
92         [NonSerialized]
93         private string _sourceQuery;
94         [NonSerialized]
95         private IEnumerable<EdmMember> _sourceQueryParamProperties; // indicates which properties populate query parameters
96
97         [NonSerialized]
98         internal bool _suppressEvents;
99         [NonSerialized]
100         internal CollectionChangeEventHandler _onAssociationChanged;
101
102         [NonSerialized]
103         private IEntityWrapper _wrappedOwner;
104
105         // ------
106         // Events
107         // ------
108
109         /// <summary>
110         /// Event to notify changes in the Associations.
111         /// </summary>
112         public event CollectionChangeEventHandler AssociationChanged
113         {
114             add
115             {
116                 CheckOwnerNull();
117                 _onAssociationChanged += value;
118             }
119             remove
120             {
121                 CheckOwnerNull();
122                 _onAssociationChanged -= value;
123             }
124         }
125
126         /// <summary>internal event to notify change in collection</summary>
127         internal virtual event CollectionChangeEventHandler AssociationChangedForObjectView
128         {
129             // we fire this event only from EntityCollection, definitely not from EntityReference
130             add { Debug.Assert(false, "should never happen"); }
131             remove { Debug.Assert(false, "should never happen"); }
132         }
133
134
135         // ----------
136         // Properties
137         // ----------
138
139
140         internal bool IsForeignKey
141         {
142             get
143             {
144                 Debug.Assert(this.ObjectContext != null, "the IsForeignKey property shouldn't be used in detached scenarios");
145                 Debug.Assert(this._relationMetadata != null, "this._relationMetadata == null");
146
147                 return ((AssociationType)_relationMetadata).IsForeignKey;
148             }
149         }
150
151         /// <summary>
152         /// This class describes a relationship navigation from the
153         /// navigation property on one entity to another entity. 
154         /// RelationshipNavigation uniquely identify a relationship type.
155         /// The RelationshipNavigation class is internal only, so this property is also internal.
156         /// See RelationshipName, SourceRoleName, and TargetRoleName for the public exposure
157         /// of the information contained in this RelationshipNavigation.
158         /// </summary>
159         internal RelationshipNavigation RelationshipNavigation
160         {
161             get
162             {
163                 return _navigation;
164             }
165         }
166
167         /// <summary>
168         /// Name of the relationship in which this RelatedEnd is participating        
169         /// </summary>
170         [System.Xml.Serialization.SoapIgnore]
171         [System.Xml.Serialization.XmlIgnore]
172         public string RelationshipName
173         {
174             get
175             {
176                 CheckOwnerNull();
177                 return _navigation.RelationshipName;
178             }
179         }
180
181         /// <summary>
182         /// Name of the relationship source role used to generate this RelatedEnd
183         /// </summary>
184         [System.Xml.Serialization.SoapIgnore]
185         [System.Xml.Serialization.XmlIgnore]
186         public string SourceRoleName
187         {
188             get
189             {
190                 CheckOwnerNull();
191                 return _navigation.From;
192             }
193         }
194
195         /// <summary>
196         /// Name of the relationship target role used to generate this RelatedEnd
197         /// </summary>
198         [System.Xml.Serialization.SoapIgnore]
199         [System.Xml.Serialization.XmlIgnore]
200         public string TargetRoleName
201         {
202             get
203             {
204                 CheckOwnerNull();
205                 return _navigation.To;
206             }
207         }
208
209         IEnumerable IRelatedEnd.CreateSourceQuery()
210         {
211             CheckOwnerNull();
212             return this.CreateSourceQueryInternal();
213         }
214
215         internal IEntityWrapper WrappedOwner
216         {
217             get
218             {
219                 return this._wrappedOwner;
220             }
221         }
222
223         internal ObjectContext ObjectContext
224         {
225             get
226             {
227                 return this._context;
228             }
229         }
230
231         internal virtual void BulkDeleteAll(System.Collections.Generic.List<object> list)
232         {
233             throw EntityUtil.NotSupported();
234         }
235
236         /// <summary>
237         /// Returns the relationship metadata associated with this RelatedEnd.
238         /// This value is available once the RelatedEnd is attached to an ObjectContext
239         /// or is retrieved with MergeOption.NoTracking
240         /// </summary>
241         [System.Xml.Serialization.SoapIgnore]
242         [System.Xml.Serialization.XmlIgnore]
243         public RelationshipSet RelationshipSet
244         {
245             get
246             {
247                 CheckOwnerNull();
248                 return this._relationshipSet;
249             }
250         }
251
252         internal RelationshipType RelationMetadata
253         {
254             get
255             {
256                 return this._relationMetadata;
257             }
258         }
259
260         internal RelationshipEndMember ToEndMember
261         {
262             get
263             {
264                 return this._toEndProperty;
265             }
266         }
267
268         internal bool UsingNoTracking
269         {
270             get
271             {
272                 return this._usingNoTracking;
273             }
274         }
275
276         internal MergeOption DefaultMergeOption
277         {
278             get
279             {
280                 return UsingNoTracking ? MergeOption.NoTracking : MergeOption.AppendOnly;
281             }
282         }
283
284         internal RelationshipEndMember FromEndProperty
285         {
286             get
287             {
288                 return this._fromEndProperty;
289             }
290         }
291
292         /// <summary>
293         /// IsLoaded returns true if and only if Load was called.
294         /// </summary>
295         [System.Xml.Serialization.SoapIgnore]
296         [System.Xml.Serialization.XmlIgnore]
297         public bool IsLoaded
298         {
299             get
300             {
301                 CheckOwnerNull();
302                 return this._isLoaded;
303             }
304         }
305
306         internal void SetIsLoaded(bool value)
307         {
308             this._isLoaded = value;
309         }
310
311         /// <summary>
312         /// This is the query which represents the source of the
313         /// related end.  It is constructed on demand using the
314         /// _connection and _cache fields and a query string based on
315         /// the type of related end and the metadata passed into its
316         /// constructor indicating the particular EDM construct the
317         /// related end models. This method is called by both subclasses of this type
318         /// and those subclasses pass in their generic type parameter in order
319         /// to produce an ObjectQuery of the right type. This allows this common 
320         /// functionality to be implemented here in the base class while still
321         /// allowing the base class to be non-generic.
322         /// </summary>
323         /// <param name="mergeOption">MergeOption to use when creating the query</param>
324         /// <param name="hasResults">Indicates whether the query can produce results.
325         /// For instance, a lookup with null key values cannot produce results.</param>
326         /// <returns>The query loading related entities.</returns>
327         internal ObjectQuery<TEntity> CreateSourceQuery<TEntity>(MergeOption mergeOption, out bool hasResults)
328         {
329             // must have a context
330             if (_context == null)
331             {
332                 hasResults = false;
333                 return null;
334             }
335
336             EntityEntry stateEntry = _context.ObjectStateManager.FindEntityEntry(_wrappedOwner.Entity);
337             EntityState entityState;
338             if (stateEntry == null)
339             {
340                 if (UsingNoTracking)
341                 {
342                     entityState = EntityState.Detached;
343                 }
344                 else
345                 {
346                     throw EntityUtil.InvalidEntityStateSource();
347                 }
348             }
349             else
350             {
351                 Debug.Assert(stateEntry != null, "Entity should exist in the current context");
352                 entityState = stateEntry.State;
353             }
354
355             //Throw incase entity is in added state, unless this is the dependent end of an FK relationship
356             if (entityState == EntityState.Added &&
357                 (!IsForeignKey ||
358                  !IsDependentEndOfReferentialConstraint(checkIdentifying: false)))
359             {
360                 throw EntityUtil.InvalidEntityStateSource();
361             }
362
363             Debug.Assert(!(entityState != EntityState.Detached && UsingNoTracking), "Entity with NoTracking option cannot exist in the ObjectStateManager");
364
365             // the CreateSourceQuery method can only return non-NULL when we're
366             // either detached & mergeOption is NoTracking or
367             // Added/Modified/Unchanged/Deleted and mergeOption is NOT NoTracking
368             // (if entity is attached to the context, mergeOption should never be NoTracking)
369             // If the entity state is added, at this point it is an FK dependent end
370             if (!((entityState == EntityState.Detached && UsingNoTracking) ||
371                    entityState == EntityState.Modified ||
372                    entityState == EntityState.Unchanged ||
373                    entityState == EntityState.Deleted ||
374                    entityState == EntityState.Added))
375             {
376                 hasResults = false;
377                 return null;
378             }
379
380             // Construct a new source query and return it.
381             Debug.Assert(_relationshipSet != null, "If we are attached to a context, we should have a relationship set.");
382
383             // Retrieve the entity key of the owner.
384             EntityKey key = _wrappedOwner.EntityKey;
385             EntityUtil.CheckEntityKeyNull(key); // Might be null because of faulty IPOCO implementation
386
387             // If the source query text has not been initialized, then do so now.
388             if (null == _sourceQuery)
389             {
390                 Debug.Assert(_relationshipSet.BuiltInTypeKind == BuiltInTypeKind.AssociationSet, "Non-AssociationSet Relationship Set?");
391                 AssociationType associationMetadata = (AssociationType)_relationMetadata;
392
393                 EntitySet ownerEntitySet = ((AssociationSet)_relationshipSet).AssociationSetEnds[_fromEndProperty.Name].EntitySet;
394                 EntitySet targetEntitySet = ((AssociationSet)_relationshipSet).AssociationSetEnds[_toEndProperty.Name].EntitySet;
395
396                 EntityType targetEntityType = MetadataHelper.GetEntityTypeForEnd((AssociationEndMember)_toEndProperty);
397                 bool ofTypeRequired = false;
398                 if (!targetEntitySet.ElementType.EdmEquals(targetEntityType) &&
399                     !TypeSemantics.IsSubTypeOf(targetEntitySet.ElementType, targetEntityType))
400                 {
401                     // If the type contained in the target entity set is not equal to
402                     // or a subtype of the referenced type, then an OfType must be
403                     // applied to the target entityset to yield only those elements that
404                     // are of the referenced type or a subtype of the referenced type.
405                     ofTypeRequired = true;
406
407                     // The type name used in the OfType clause must be the name of the
408                     // corresponding O-Space Entity type, since the source query will be
409                     // parsed using the CLR perspective (by ObjectQuery).
410                     TypeUsage targetOSpaceTypeUsage = ObjectContext.MetadataWorkspace.GetOSpaceTypeUsage(TypeUsage.Create(targetEntityType));
411                     targetEntityType = (EntityType)targetOSpaceTypeUsage.EdmType;
412                 }
413
414                 StringBuilder sourceBuilder;
415                 if (associationMetadata.IsForeignKey)
416                 {
417                     var fkConstraint = associationMetadata.ReferentialConstraints[0];
418                     var principalProps = fkConstraint.FromProperties;
419                     var dependentProps = fkConstraint.ToProperties;
420                     Debug.Assert(principalProps.Count == dependentProps.Count, "Mismatched foreign key properties?");
421
422                     if (fkConstraint.ToRole.EdmEquals(_toEndProperty))
423                     {
424                         // This related end goes from 'principal' to 'dependent', and has the key of the principal.
425                         // In this case it is sufficient to filter the target (dependent) set where the foreign key
426                         // properties have the same values as the corresponding entity key properties from the principal.
427                         //
428                         // SELECT VALUE D 
429                         //   FROM OfType(##DependentEntityset, ##DependentEntityType) 
430                         // AS D
431                         // WHERE 
432                         //   D.DependentProperty1 = @PrincipalProperty1 [AND
433                         //   ...
434                         //   D.DependentPropertyN = @PrincipalPropertyN]
435                         //
436                         // Note that the OfType operator can be omitted if the element type of ##DependentEntitySet
437                         // is equal to the Entity type produced by the target end of the relationship.
438                         sourceBuilder = new StringBuilder("SELECT VALUE D FROM ");
439                         AppendEntitySet(sourceBuilder, targetEntitySet, targetEntityType, ofTypeRequired);
440                         sourceBuilder.Append(" AS D WHERE ");
441
442                         // For each principal key property there is a corresponding query parameter that supplies the value
443                         // from this owner's entity key, so KeyParam1 corresponds to the first key member, etc.
444                         // We remember the order of the corresponding principal key values in the _sourceQueryParamProperties
445                         // field.
446                         AliasGenerator keyParamNameGen = new AliasGenerator(_entityKeyParamName); // Aliases are cached in AliasGenerator
447                         _sourceQueryParamProperties = principalProps;
448
449                         for (int idx = 0; idx < dependentProps.Count; idx++)
450                         {
451                             if (idx > 0)
452                             {
453                                 sourceBuilder.Append(" AND ");
454                             }
455
456                             sourceBuilder.Append("D.[");
457                             sourceBuilder.Append(dependentProps[idx].Name);
458                             sourceBuilder.Append("] = @");
459                             sourceBuilder.Append(keyParamNameGen.Next());
460                         }
461                     }
462                     else
463                     {
464                         // This related end goes from 'dependent' to 'principal', and has the key of the dependent
465                         // In this case it is necessary to filter the target (principal) entity set on the foreign 
466                         // key relationship properties to retrieve the corresponding principal entity.
467                         //
468                         // SELECT VALUE P FROM
469                         //   OfType(##PrincipalEntityset, ##PrincipalEntityType) AS P
470                         // WHERE 
471                         //   P.PrincipalProperty1 = @DependentProperty1 AND ...
472                         //
473                         Debug.Assert(fkConstraint.FromRole.EdmEquals(_toEndProperty), "Source query for foreign key association related end is not based on principal or dependent?");
474
475                         sourceBuilder = new StringBuilder("SELECT VALUE P FROM ");
476                         AppendEntitySet(sourceBuilder, targetEntitySet, targetEntityType, ofTypeRequired);
477                         sourceBuilder.Append(" AS P WHERE ");
478
479                         AliasGenerator keyParamNameGen = new AliasGenerator(_entityKeyParamName); // Aliases are cached in AliasGenerator
480                         _sourceQueryParamProperties = dependentProps;
481                         for (int idx = 0; idx < principalProps.Count; idx++)
482                         {
483                             if (idx > 0)
484                             {
485                                 sourceBuilder.Append(" AND ");
486                             }
487                             sourceBuilder.Append("P.[");
488                             sourceBuilder.Append(principalProps[idx].Name);
489                             sourceBuilder.Append("] = @");
490                             sourceBuilder.Append(keyParamNameGen.Next());
491                         }
492                         _sourceQuery = sourceBuilder.ToString();
493                     }
494                 }
495                 else
496                 {
497                     // Translate to:
498                     // SELECT VALUE [TargetEntity]
499                     //  FROM 
500                     //      (SELECT VALUE x FROM ##RelationshipSet AS x
501                     //       WHERE Key(x.[##SourceRoleName]) = ROW(@key1 AS key1[..., @keyN AS keyN])
502                     //       ) AS [AssociationEntry]
503                     //  INNER JOIN 
504                     //       OfType(##TargetEntityset, ##TargetRole.EntityType) AS [TargetEntity] 
505                     //  ON
506                     //       Key([AssociationEntry].##TargetRoleName) = Key(Ref([TargetEntity]))
507                     //
508                     // Note that the OfType operator can be omitted if the element type of ##TargetEntitySet
509                     // is equal to the Entity type produced by the target end of the relationship.
510
511                     sourceBuilder = new StringBuilder("SELECT VALUE [TargetEntity] FROM (SELECT VALUE x FROM ");
512                     sourceBuilder.Append("[");
513                     sourceBuilder.Append(_relationshipSet.EntityContainer.Name);
514                     sourceBuilder.Append("].[");
515                     sourceBuilder.Append(_relationshipSet.Name);
516                     sourceBuilder.Append("] AS x WHERE Key(x.[");
517                     sourceBuilder.Append(_fromEndProperty.Name);
518                     sourceBuilder.Append("]) = ");
519
520                     AppendKeyParameterRow(sourceBuilder, key.GetEntitySet(ObjectContext.MetadataWorkspace).ElementType.KeyMembers);
521
522                     sourceBuilder.Append(") AS [AssociationEntry] INNER JOIN ");
523
524                     AppendEntitySet(sourceBuilder, targetEntitySet, targetEntityType, ofTypeRequired);
525
526                     sourceBuilder.Append(" AS [TargetEntity] ON Key([AssociationEntry].[");
527                     sourceBuilder.Append(_toEndProperty.Name);
528                     sourceBuilder.Append("]) = Key(Ref([TargetEntity]))");
529                 }
530
531                 _sourceQuery = sourceBuilder.ToString();
532             }
533
534             // Create a new ObjectQuery based on the source query text, the object context, and the specified merge option.
535             ObjectQuery<TEntity> query = new ObjectQuery<TEntity>(_sourceQuery, _context, mergeOption);
536
537             // Add a parameter for each entity key value found on the key.
538             AliasGenerator paramNameGen = new AliasGenerator(_entityKeyParamName); // Aliases are cached in AliasGenerator
539             IEnumerable<EdmMember> parameterMembers = _sourceQueryParamProperties
540                 ?? key.GetEntitySet(ObjectContext.MetadataWorkspace).ElementType.KeyMembers;
541
542             hasResults = true;
543             foreach (EdmMember parameterMember in parameterMembers)
544             {
545                 // Create a new ObjectParameter with the next parameter name and the next entity value.
546                 // When _sourceQueryParamProperties are defined, it means we are handling a foreign key association. For an FK association,
547                 // the current entity values are considered truth. Otherwise, we use EntityKey values for backwards
548                 // compatibility with independent association behaviors in .NET 3.5.
549                 object value;
550                 if (null == _sourceQueryParamProperties)
551                 {
552                     // retrieve the value from the entity key (independent association lookup)
553                     value = _wrappedOwner.EntityKey.EntityKeyValues.Single(ekv => ekv.Key == parameterMember.Name).Value;
554                 }
555                 else
556                 {
557                     // retrieve the value from the entity itself (FK lookup)
558                     EntityReference reference = this as EntityReference;
559                     if (reference != null && ForeignKeyFactory.IsConceptualNullKey(reference.CachedForeignKey))
560                     {
561                         value = null;
562                     }
563                     else
564                     {
565                         value = GetCurrentValueFromEntity(parameterMember);
566                     }
567                 }
568                 ObjectParameter queryParam;
569                 if (null == value)
570                 {
571                     var parameterEdmType = parameterMember.TypeUsage.EdmType;
572                     Debug.Assert(Helper.IsScalarType(parameterEdmType), "Only primitive or enum type expected for parameters");
573
574                     Type parameterClrType = Helper.IsPrimitiveType(parameterEdmType) ? 
575                         ((PrimitiveType)parameterEdmType).ClrEquivalentType :
576                         ((ClrEnumType)ObjectContext.MetadataWorkspace.GetObjectSpaceType((EnumType)parameterEdmType)).ClrType;
577
578                     queryParam = new ObjectParameter(paramNameGen.Next(), parameterClrType);
579                     // If any lookup value is null, the query cannot match any rows.
580                     hasResults = false;
581                 }
582                 else
583                 {
584                     queryParam = new ObjectParameter(paramNameGen.Next(), value);
585                 }
586
587                 // Map the type of the key member to C-Space and explicitly specify this mapped type
588                 // as the effective type of the new ObjectParameter - this is required so that the
589                 // type of the key value parameter matches the declared type of the key member when
590                 // the query text is parsed.
591                 queryParam.TypeUsage = Helper.GetModelTypeUsage(parameterMember);
592
593                 // Add the new parameter to the Parameters collection of the query.
594                 query.Parameters.Add(queryParam);
595             }
596
597             // It should not be possible to add or remove parameters from the new query, since the query text
598             // is fixed. Adding or removing parameters will likely make the query fail to execute.
599             query.Parameters.SetReadOnly(true);
600
601             // Return the new ObjectQuery. Note that this is intentionally a tear-off so that any changes made
602             // to its Parameters collection (or the ObjectParameters themselves) have no effect on anyone else
603             // that may retrieve this query - each access will always return a new ObjectQuery instance.
604             return query;
605         }
606
607         private object GetCurrentValueFromEntity(EdmMember member)
608         {
609             // retrieve member accessor from the object context (which already keeps track of the relevant
610             // metadata)
611             StateManagerTypeMetadata metaType = _context.ObjectStateManager.GetOrAddStateManagerTypeMetadata(member.DeclaringType);
612             StateManagerMemberMetadata metaMember = metaType.Member(metaType.GetOrdinalforCLayerMemberName(member.Name));
613             return metaMember.GetValue(_wrappedOwner.Entity);
614         }
615
616         private static void AppendKeyParameterRow(StringBuilder sourceBuilder, IList<EdmMember> keyMembers)
617         {
618             sourceBuilder.Append("ROW(");
619             AliasGenerator keyParamNameGen = new AliasGenerator(_entityKeyParamName); // Aliases are cached in AliasGenerator
620             int keyMemberCount = keyMembers.Count;
621             for (int idx = 0; idx < keyMemberCount; idx++)
622             {
623                 string keyParamName = keyParamNameGen.Next();
624                 sourceBuilder.Append("@");
625                 sourceBuilder.Append(keyParamName);
626                 sourceBuilder.Append(" AS ");
627                 sourceBuilder.Append(keyParamName);
628
629                 if (idx < keyMemberCount - 1)
630                 {
631                     sourceBuilder.Append(",");
632                 }
633             }
634             sourceBuilder.Append(")");
635         }
636
637         private static void AppendEntitySet(StringBuilder sourceBuilder, EntitySet targetEntitySet, EntityType targetEntityType, bool ofTypeRequired)
638         {
639             if (ofTypeRequired)
640             {
641                 sourceBuilder.Append("OfType(");
642             }
643             sourceBuilder.Append("[");
644             sourceBuilder.Append(targetEntitySet.EntityContainer.Name);
645             sourceBuilder.Append("].[");
646             sourceBuilder.Append(targetEntitySet.Name);
647             sourceBuilder.Append("]");
648             if (ofTypeRequired)
649             {
650                 sourceBuilder.Append(", [");
651                 if (targetEntityType.NamespaceName != string.Empty)
652                 {
653                     sourceBuilder.Append(targetEntityType.NamespaceName);
654                     sourceBuilder.Append("].[");
655                 }
656                 sourceBuilder.Append(targetEntityType.Name);
657                 sourceBuilder.Append("])");
658             }
659         }
660
661         /// <summary>
662         /// Validates that a call to Load has the correct conditions
663         /// This helps to reduce the complexity of the Load call (SQLBU 524128)
664         /// </summary>
665         /// <returns>See RelatedEnd.CreateSourceQuery method. This is returned here so we can create it and validate the state before returning it to the caller</returns>
666         internal ObjectQuery<TEntity> ValidateLoad<TEntity>(MergeOption mergeOption, string relatedEndName, out bool hasResults)
667         {
668             ObjectQuery<TEntity> sourceQuery = CreateSourceQuery<TEntity>(mergeOption, out hasResults);
669             if (null == sourceQuery)
670             {
671                 throw EntityUtil.RelatedEndNotAttachedToContext(relatedEndName);
672             }
673
674             EntityEntry entry = ObjectContext.ObjectStateManager.FindEntityEntry(_wrappedOwner.Entity);
675             //Throw in case entity is in deleted state
676             if (entry != null && entry.State == EntityState.Deleted)
677             {
678                 throw EntityUtil.InvalidEntityStateLoad(relatedEndName);
679             }
680
681             // MergeOption for Load must be NoTracking if and only if the source entity was NoTracking. If the source entity was 
682             // retrieved with any other MergeOption, the Load MergeOption can be anything but NoTracking. I.e. The entity could
683             // have been loaded with OverwriteChanges and the Load option can be AppendOnly.
684             if (UsingNoTracking != (mergeOption == MergeOption.NoTracking))
685             {
686                 throw EntityUtil.MismatchedMergeOptionOnLoad(mergeOption);
687             }
688
689             if (UsingNoTracking)
690             {
691                 if (this.IsLoaded)
692                 {
693                     throw EntityUtil.LoadCalledOnAlreadyLoadedNoTrackedRelatedEnd();
694                 }
695
696                 if (!IsEmpty())
697                 {
698                     throw EntityUtil.LoadCalledOnNonEmptyNoTrackedRelatedEnd();
699                 }
700             }
701
702             return sourceQuery;
703         }
704
705         // -------
706         // Methods
707         // -------
708
709         /// <summary>
710         /// Loads the related entity or entities into the local related end using the default merge option.        
711         /// </summary>
712         public void Load()
713         {
714             CheckOwnerNull();
715             Load(DefaultMergeOption);
716         }
717
718         /// <summary>
719         /// Loads the related entity or entities into the local related end using the supplied MergeOption.        
720         /// </summary>
721         public abstract void Load(MergeOption mergeOption);
722
723         /// <summary>
724         /// 
725         /// </summary>
726         internal void DeferredLoad()
727         {
728             if (_wrappedOwner != null && _wrappedOwner != EntityWrapperFactory.NullWrapper &&
729                 !IsLoaded &&
730                 _context != null &&
731                 _context.ContextOptions.LazyLoadingEnabled &&
732                 !_context.InMaterialization &&
733                 CanDeferredLoad)
734             {
735                 // Ensure the parent EntityState is NoTracking, Unchanged, or Modified
736                 // Detached, Added, and Deleted parents cannot call Load
737                 Debug.Assert(_wrappedOwner != null, "Wrapper owner should never be null");
738                 if (UsingNoTracking ||
739                     (_wrappedOwner.ObjectStateEntry != null &&
740                      (_wrappedOwner.ObjectStateEntry.State == EntityState.Unchanged ||
741                       _wrappedOwner.ObjectStateEntry.State == EntityState.Modified ||
742                       (_wrappedOwner.ObjectStateEntry.State == EntityState.Added && IsForeignKey && IsDependentEndOfReferentialConstraint(false)))))
743                 {
744                     // Avoid infinite recursive calls
745                     _context.ContextOptions.LazyLoadingEnabled = false;
746                     try
747                     {
748                         Load();
749                     }
750                     finally
751                     {
752                         _context.ContextOptions.LazyLoadingEnabled = true;
753                     }
754                 }
755             }
756         }
757
758         internal virtual bool CanDeferredLoad
759         {
760             get { return true; }
761         }
762
763         /// <summary>
764         /// Takes a list of related entities and merges them into the current collection.
765         /// </summary>        
766         /// <param name="collection">Entities to relate to the owner of this EntityCollection</param>
767         /// <param name="mergeOption">MergeOption to use when updating existing relationships</param>
768         /// <param name="setIsLoaded">Indicates whether IsLoaded should be set to true after the Load is complete.
769         /// Should be false in cases where we cannot guarantee that the set of entities is complete
770         /// and matches the server, such as Attach.</param>
771         internal void Merge<TEntity>(IEnumerable<TEntity> collection, MergeOption mergeOption, bool setIsLoaded)
772         {
773             List<IEntityWrapper> refreshedCollection = collection as List<IEntityWrapper>;
774             if (refreshedCollection == null)
775             {
776                 refreshedCollection = new List<IEntityWrapper>();
777                 EntitySet targetEntitySet = ((AssociationSet)RelationshipSet).AssociationSetEnds[TargetRoleName].EntitySet;
778                 foreach (TEntity entity in collection)
779                 {
780                     IEntityWrapper wrapper = EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext);
781                     // When the MergeOption is NoTraking, we need to make sure the wrapper reflects the current context and
782                     // has an EntityKey
783                     if (mergeOption == MergeOption.NoTracking)
784                     {
785                         EntityWrapperFactory.UpdateNoTrackingWrapper(wrapper, ObjectContext, targetEntitySet);
786                     }
787                     refreshedCollection.Add(wrapper);
788                 }
789             }
790             Merge<TEntity>(refreshedCollection, mergeOption, setIsLoaded);
791         }
792
793         // Internal version of Merge that works on wrapped entities.
794         internal void Merge<TEntity>(List<IEntityWrapper> collection, MergeOption mergeOption, bool setIsLoaded)
795         {
796             //Dev note: do not add event firing in Merge API, if it need to be added, add it to the caller
797             EntityKey sourceKey = _wrappedOwner.EntityKey;
798             EntityUtil.CheckEntityKeyNull(sourceKey);
799
800             ObjectStateManager.UpdateRelationships(this.ObjectContext, mergeOption, (AssociationSet)RelationshipSet, (AssociationEndMember)FromEndProperty, sourceKey, _wrappedOwner, (AssociationEndMember)ToEndMember, collection, setIsLoaded);
801
802             if (setIsLoaded)
803             {
804                 // If the input collection contains all related entities, mark the collection as "loaded"
805                 _isLoaded = true;
806             }
807         }
808
809         /// <summary>
810         /// Attaches an entity to the related end.  This method works in exactly the same way as Attach(object).
811         /// It is maintained for backward compatibility with previous versions of IRelatedEnd.
812         /// </summary>
813         /// <param name="entity">The entity to attach to the related end</param>
814         /// <exception cref="ArgumentNullException">Thrown when <paramref name="entity"/> is null.</exception>
815         /// <exception cref="InvalidOperationException">Thrown when the entity cannot be related via the current relationship end.</exception>
816         void IRelatedEnd.Attach(IEntityWithRelationships entity)
817         {
818             ((IRelatedEnd)this).Attach((object)entity);
819         }
820
821         /// <summary>
822         /// Attaches an entity to the related end. If the related end is already filled
823         /// or partially filled, this merges the existing entities with the given entity. The given
824         /// entity is not assumed to be the complete set of related entities.
825         /// 
826         /// Owner and all entities passed in must be in Unchanged or Modified state. 
827         /// Deleted elements are allowed only when the state manager is already tracking the relationship
828         /// instance.
829         /// </summary>
830         /// <param name="entity">The entity to attach to the related end</param>
831         /// <exception cref="ArgumentNullException">Thrown when <paramref name="entity"/> is null.</exception>
832         /// <exception cref="InvalidOperationException">Thrown when the entity cannot be related via the current relationship end.</exception>
833         void IRelatedEnd.Attach(object entity)
834         {
835             CheckOwnerNull();
836             EntityUtil.CheckArgumentNull(entity, "entity");
837             Attach(new IEntityWrapper[] { EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext) }, false);
838         }
839
840         internal void Attach(IEnumerable<IEntityWrapper> wrappedEntities, bool allowCollection)
841         {
842             CheckOwnerNull();
843             ValidateOwnerForAttach();
844
845             // validate children and collect them in the "refreshedCollection" for this instance
846             int index = 0;
847             List<IEntityWrapper> collection = new List<IEntityWrapper>();
848
849             foreach (IEntityWrapper entity in wrappedEntities)
850             {
851                 ValidateEntityForAttach(entity, index++, allowCollection);
852                 collection.Add(entity);
853             }
854
855             _suppressEvents = true;
856             try
857             {
858                 // After Attach, the two entities should be related in the Unchanged state, so use OverwriteChanges
859                 // Since no query is done in this case, the MergeOption only controls the relationships
860                 Merge(collection, MergeOption.OverwriteChanges, false /*setIsLoaded*/);
861                 ReferentialConstraint constraint = ((AssociationType)RelationMetadata).ReferentialConstraints.FirstOrDefault();
862                 if (constraint != null)
863                 {
864                     ObjectStateManager stateManager = ObjectContext.ObjectStateManager;
865                     EntityEntry ownerEntry = stateManager.FindEntityEntry(_wrappedOwner.Entity);
866                     Debug.Assert(ownerEntry != null, "Both entities should be attached.");
867                     if (IsDependentEndOfReferentialConstraint(checkIdentifying: false))
868                     {
869                         Debug.Assert(collection.Count == 1, "Dependant should attach to single principal");
870                         if (!VerifyRIConstraintsWithRelatedEntry(constraint, ownerEntry.GetCurrentEntityValue, collection[0].ObjectStateEntry.EntityKey))
871                         {
872                             throw EntityUtil.InconsistentReferentialConstraintProperties();
873                         }
874                     }
875                     else
876                     {
877                         foreach (IEntityWrapper wrappedTarget in collection)
878                         {
879                             RelatedEnd targetRelatedEnd = GetOtherEndOfRelationship(wrappedTarget);
880                             if (targetRelatedEnd.IsDependentEndOfReferentialConstraint(checkIdentifying: false))
881                             {
882                                 EntityEntry targetEntry = stateManager.FindEntityEntry(((EntityReference)targetRelatedEnd).WrappedOwner.Entity);
883                                 Debug.Assert(targetEntry != null, "Both entities should be attached.");
884                                 if (!VerifyRIConstraintsWithRelatedEntry(constraint, targetEntry.GetCurrentEntityValue, ownerEntry.EntityKey))
885                                 {
886                                     throw EntityUtil.InconsistentReferentialConstraintProperties();
887                                 }
888                             }
889                         }
890                     }
891                 }
892             }
893             finally
894             {
895                 _suppressEvents = false;
896             }
897             OnAssociationChanged(CollectionChangeAction.Refresh, null);
898         }
899
900         // verifies requirements for Owner in Attach()
901         internal void ValidateOwnerForAttach()
902         {
903             if (null == this.ObjectContext || UsingNoTracking)
904             {
905                 throw EntityUtil.InvalidOwnerStateForAttach();
906             }
907
908             // find state entry
909             EntityEntry stateEntry = this.ObjectContext.ObjectStateManager.GetEntityEntry(_wrappedOwner.Entity);
910             if (stateEntry.State != EntityState.Modified &&
911                 stateEntry.State != EntityState.Unchanged)
912             {
913                 throw EntityUtil.InvalidOwnerStateForAttach();
914             }
915         }
916
917         // verifies requirements for child entity passed to Attach()
918         internal void ValidateEntityForAttach(IEntityWrapper wrappedEntity, int index, bool allowCollection)
919         {
920             if (null == wrappedEntity || null == wrappedEntity.Entity)
921             {
922                 if (allowCollection)
923                 {
924                     throw EntityUtil.InvalidNthElementNullForAttach(index);
925                 }
926                 else
927                 {
928                     throw EntityUtil.ArgumentNull("wrappedEntity");
929                 }
930             }
931
932             // Having this verification here results in having the same exception no matter how the further code path is changed.
933             this.VerifyType(wrappedEntity);
934
935             // verify the entity exists in the current context
936             Debug.Assert(null != this.ObjectContext,
937                 "ObjectContext must not be null after call to ValidateOwnerForAttach");
938             Debug.Assert(!UsingNoTracking, "We should not be here for NoTracking case.");
939             EntityEntry stateEntry = ObjectContext.ObjectStateManager.FindEntityEntry(wrappedEntity.Entity);
940             if (null == stateEntry || !Object.ReferenceEquals(stateEntry.Entity, wrappedEntity.Entity))
941             {
942                 if (allowCollection)
943                 {
944                     throw EntityUtil.InvalidNthElementContextForAttach(index);
945                 }
946                 else
947                 {
948                     throw EntityUtil.InvalidEntityContextForAttach();
949                 }
950             }
951             Debug.Assert(stateEntry.State != EntityState.Detached,
952                 "State cannot be detached if the entry was retrieved from the context");
953
954             // verify the state of the entity (may not be in added state, since we only support attaching relationships
955             // to existing entities)
956             if (stateEntry.State != EntityState.Unchanged &&
957                 stateEntry.State != EntityState.Modified)
958             {
959                 if (allowCollection)
960                 {
961                     throw EntityUtil.InvalidNthElementStateForAttach(index);
962                 }
963                 else
964                 {
965                     throw EntityUtil.InvalidEntityStateForAttach();
966                 }
967             }
968         }
969
970         internal abstract IEnumerable CreateSourceQueryInternal();
971
972         /// <summary>
973         /// Adds an entity to the related end.  This method works in exactly the same way as Add(object).
974         /// It is maintained for backward compatibility with previous versions of IRelatedEnd.
975         /// </summary>
976         /// <param name="entity">
977         ///   Entity instance to add to the related end
978         /// </param>
979         void IRelatedEnd.Add(IEntityWithRelationships entity)
980         {
981             ((IRelatedEnd)this).Add((object)entity);
982         }
983
984         /// <summary>
985         ///   Adds an entity to the related end.  If the owner is
986         ///   attached to a cache then the all the connected ends are
987         ///   added to the object cache and their corresponding relationships 
988         ///   are also added to the ObjectStateManager. The RelatedEnd of the
989         ///   relationship is also fixed.
990         /// </summary>
991         /// <param name="entity">
992         ///   Entity instance to add to the related end
993         /// </param>
994         void IRelatedEnd.Add(object entity)
995         {
996             EntityUtil.CheckArgumentNull(entity, "entity");
997             this.Add(EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext));
998         }
999
1000         internal void Add(IEntityWrapper wrappedEntity)
1001         {
1002             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
1003
1004             if (_wrappedOwner.Entity != null)
1005             {
1006                 Add(wrappedEntity, /*applyConstraints*/true);
1007             }
1008             else
1009             {
1010                 // The related end is in a disconnected state, so the related end is just a container
1011                 // A common scenario for this is during WCF deserialization
1012                 DisconnectedAdd(wrappedEntity);
1013             }
1014         }
1015
1016         /// <summary>
1017         /// Removes an entity from the related end.  This method works in exactly the same way as Remove(object).
1018         /// It is maintained for backward compatibility with previous versions of IRelatedEnd.
1019         /// </summary>
1020         /// <param name="entity">
1021         ///   Entity instance to remove from the related end
1022         /// </param>
1023         /// <returns>Returns true if the entity was successfully removed, false if the entity was not part of the RelatedEnd.</returns>
1024         bool IRelatedEnd.Remove(IEntityWithRelationships entity)
1025         {
1026             return ((IRelatedEnd)this).Remove((object)entity);
1027         }
1028
1029         /// <summary>
1030         ///   Removes an entity from the related end.  If owner is
1031         ///   attached to a cache, marks relationship for deletion and if
1032         ///   the relationship is composition also marks the entity for deletion.
1033         /// </summary>
1034         /// <param name="entity">
1035         ///   Entity instance to remove from the related end
1036         /// </param>
1037         /// <returns>Returns true if the entity was successfully removed, false if the entity was not part of the RelatedEnd.</returns>
1038         bool IRelatedEnd.Remove(object entity)
1039         {
1040             EntityUtil.CheckArgumentNull(entity, "entity");
1041             DeferredLoad();
1042             return Remove(EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext), false);
1043         }
1044
1045         // Internal version that works on a wrapped entity and can be called from multiple
1046         // places where the public version is no longer appropriate.
1047         internal bool Remove(IEntityWrapper wrappedEntity, bool preserveForeignKey)
1048         {
1049             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
1050
1051             if (_wrappedOwner.Entity != null)
1052             {
1053                 if (ContainsEntity(wrappedEntity))
1054                 {
1055
1056                     Remove(wrappedEntity, /*fixup*/true, /*deleteEntity*/false, /*deleteOwner*/false, /*applyReferentialConstraints*/true, preserveForeignKey);
1057                     return true;
1058                 }
1059                 // The entity is not related so return false
1060                 return false;
1061
1062             }
1063             else
1064             {
1065                 // The related end is in a disconnected state, so the related end is just a container
1066                 // A common scenario for this is during WCF deserialization
1067                 return DisconnectedRemove(wrappedEntity);
1068             }
1069         }
1070
1071
1072         internal abstract void DisconnectedAdd(IEntityWrapper wrappedEntity);
1073         internal abstract bool DisconnectedRemove(IEntityWrapper wrappedEntity);
1074
1075         internal void Add(IEntityWrapper wrappedEntity, bool applyConstraints)
1076         {
1077             // SQLBU: 508819 508813 508752
1078             // Detect as soon as possible if we are trying to re-add entities which are in Deleted state.
1079             // When one of the entity is in Deleted state, attempt would be made to re-add this entity
1080             // to the OSM which is not allowed.
1081             // NOTE: Current cleaning code (which uses cleanupOwnerEntity and cleanupPassedInEntity) 
1082             // works only if one of the entity is not attached to the context.
1083             // PERFORMANCE: following can be performed faster if ObjectStateManager provide method to
1084             // lookup only in dictionary with Deleted entities (because here we are interested only in Deleted entities)
1085             if (_context != null && !UsingNoTracking)
1086             {
1087                 ValidateStateForAdd(_wrappedOwner);
1088                 ValidateStateForAdd(wrappedEntity);
1089             }
1090
1091             this.Add(wrappedEntity,
1092                 applyConstraints: applyConstraints,
1093                 addRelationshipAsUnchanged: false,
1094                 relationshipAlreadyExists: false,
1095                 allowModifyingOtherEndOfRelationship: true,
1096                 forceForeignKeyChanges: true);
1097
1098         }
1099
1100         internal void CheckRelationEntitySet(EntitySet set)
1101         {
1102             Debug.Assert(set != null, "null EntitySet");
1103             Debug.Assert(_relationshipSet != null,
1104                 "Should only be checking the RelationshipSet on an attached entity and it should always be non-null in that case");
1105
1106             if ((((AssociationSet)_relationshipSet).AssociationSetEnds[_navigation.To] != null) &&
1107                 (((AssociationSet)_relationshipSet).AssociationSetEnds[_navigation.To].EntitySet != set))
1108             {
1109                 throw EntityUtil.EntitySetIsNotValidForRelationship(set.EntityContainer.Name, set.Name, _navigation.To, _relationshipSet.EntityContainer.Name, _relationshipSet.Name);
1110             }
1111         }
1112
1113         internal void ValidateStateForAdd(IEntityWrapper wrappedEntity)
1114         {
1115             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
1116             EntityEntry entry = this.ObjectContext.ObjectStateManager.FindEntityEntry(wrappedEntity.Entity);
1117             if (entry != null && entry.State == EntityState.Deleted)
1118             {
1119                 throw EntityUtil.UnableToAddRelationshipWithDeletedEntity();
1120             }
1121         }
1122
1123         internal void Add(IEntityWrapper wrappedTarget,
1124             bool applyConstraints,
1125             bool addRelationshipAsUnchanged,
1126             bool relationshipAlreadyExists,
1127             bool allowModifyingOtherEndOfRelationship,   // needed by ChangeRelationshipState - check multiplicity constraints instead of silently updating other end of relationship
1128             bool forceForeignKeyChanges)
1129         {
1130             Debug.Assert(wrappedTarget != null, "IEntityWrapper instance is null.");
1131             // Do verification
1132             if (!this.VerifyEntityForAdd(wrappedTarget, relationshipAlreadyExists))
1133             {
1134                 // Allow the same item to be "added" to a collection as a no-op operation
1135                 return;
1136             }
1137
1138             EntityKey key = wrappedTarget.EntityKey;
1139             if ((object)key != null && ObjectContext != null)
1140             {
1141                 CheckRelationEntitySet(key.GetEntitySet(ObjectContext.MetadataWorkspace));
1142             }
1143
1144             RelatedEnd targetRelatedEnd = GetOtherEndOfRelationship(wrappedTarget);
1145
1146             if (Object.ReferenceEquals(this.ObjectContext, targetRelatedEnd.ObjectContext) && this.ObjectContext != null)
1147             {
1148                 // Both entities are associated with the same non-null context
1149
1150                 // Make sure that they are either both tracked or both not tracked, or both don't have contexts
1151                 if (UsingNoTracking != targetRelatedEnd.UsingNoTracking)
1152                 {
1153                     throw EntityUtil.CannotCreateRelationshipBetweenTrackedAndNoTrackedEntities(UsingNoTracking ?
1154                         this._navigation.From : this._navigation.To);
1155                 }
1156             }
1157             else if (this.ObjectContext != null && targetRelatedEnd.ObjectContext != null)
1158             {
1159                 // Both entities have a context
1160                 if (UsingNoTracking && targetRelatedEnd.UsingNoTracking)
1161                 {
1162                     // Both entities are NoTracking, but have different contexts
1163                     // Attach the owner's context to the target's RelationshipManager
1164                     // O-C mappings are 1:1, so this operation is allowed
1165                     wrappedTarget.ResetContext(this.ObjectContext, GetTargetEntitySetFromRelationshipSet(), MergeOption.NoTracking);
1166                 }
1167                 else
1168                 {
1169                     // Both entities are already tracked by different non-null contexts
1170                     throw EntityUtil.CannotCreateRelationshipEntitiesInDifferentContexts();
1171                 }
1172             }
1173             else if ((_context == null || UsingNoTracking) && (targetRelatedEnd.ObjectContext != null && !targetRelatedEnd.UsingNoTracking))
1174             {
1175                 // Only the target has a context, so validate it is in a suitable state
1176                 targetRelatedEnd.ValidateStateForAdd(targetRelatedEnd.WrappedOwner);
1177             }
1178
1179             targetRelatedEnd.VerifyEntityForAdd(_wrappedOwner, relationshipAlreadyExists);
1180
1181
1182             // Do actual add
1183
1184             // Perform multiplicity constraints verification for the target related end before current related end is modified.
1185             // The "allowModifyingOtherEndOfRelationship" is used by ObjectStateManager.ChangeRelationshipState.
1186             targetRelatedEnd.VerifyMultiplicityConstraintsForAdd(!allowModifyingOtherEndOfRelationship);
1187
1188             // Add the target entity to the source entity's collection or reference
1189             if (this.CheckIfNavigationPropertyContainsEntity(wrappedTarget))
1190             {
1191                 this.AddToLocalCache(wrappedTarget, applyConstraints);
1192             }
1193             else
1194             {
1195                 this.AddToCache(wrappedTarget, applyConstraints);
1196             }
1197
1198             // Fix up the target end of the relationship by adding the source entity to the target entity's collection or reference
1199             // devnote: applyConstraints should be always false to enable scenarios like this:
1200             //             orderLine.Order = order1;
1201             //             order2.OrderLines.Add(orderLine); // orderLine.Order is changed to order2
1202             if (targetRelatedEnd.CheckIfNavigationPropertyContainsEntity(WrappedOwner))
1203             {
1204                 // Example: IPOCO order, POCO customer with a bidirectional relationship
1205                 //  customer.Orders.Add(order);
1206                 //  order.Customer = customer <-- the Orders collection already contains "order" on fixup and this would add a duplicate
1207                 targetRelatedEnd.AddToLocalCache(_wrappedOwner, /*applyConstraints*/ false);
1208             }
1209             else
1210             {
1211                 targetRelatedEnd.AddToCache(_wrappedOwner, /*applyConstraints*/ false);
1212             }
1213             // delay event firing for targetRelatedEnd. once we fire the event, we should be at operation completed state
1214
1215             // Ensure that both entities end up in the same context:
1216             // (1) If neither entity is attached to a context, we don't need to do anything else.
1217             // (2) If they are both in the same one, we need to make sure neither one was created with MergeOption.NoTracking,
1218             //     and if not, add a relationship entry if it doesn't already exist.
1219             // (3) If both entities are already in different contexts, fail.            
1220             // (4) Otherwise, only one entity is attached, and that is the context we will use.
1221             //     For the entity that is not attached, attach it to that context.    
1222
1223             RelatedEnd attachedRelatedEnd = null; // the end of the relationship that is already attached to a context, if there is one.
1224             IEntityWrapper entityToAdd = null; // the entity to be added to attachedRelatedEnd
1225
1226             if (Object.ReferenceEquals(this.ObjectContext, targetRelatedEnd.ObjectContext) && this.ObjectContext != null)
1227             {
1228                 // Both entities are associated with the same non-null context
1229
1230                 // Make sure that a relationship entry exists between these two entities. It is possible that the entities could
1231                 // have been added to the context independently of each other, so the relationship may not exist yet.
1232                 if (!this.IsForeignKey && !relationshipAlreadyExists && !UsingNoTracking)
1233                 {
1234                     // If this Add is triggered by setting the principle end of an unchanged/modified dependent end, then the relationship should be Unchanged
1235                     if (!this.ObjectContext.ObjectStateManager.TransactionManager.IsLocalPublicAPI &&
1236                         this.WrappedOwner.EntityKey != null &&
1237                         !this.WrappedOwner.EntityKey.IsTemporary && IsDependentEndOfReferentialConstraint(false))
1238                     {
1239                         addRelationshipAsUnchanged = true;
1240                     }
1241
1242                     AddRelationshipToObjectStateManager(wrappedTarget, addRelationshipAsUnchanged, /*doAttach*/false);
1243                 }
1244
1245                 // The condition (IsAddTracking || IsAttachTracking || IsDetectChanges) excludes the case
1246                 // when the method is called from materialization when we don't want to verify the navigation property.
1247                 if (wrappedTarget.RequiresRelationshipChangeTracking &&
1248                     (this.ObjectContext.ObjectStateManager.TransactionManager.IsAddTracking ||
1249                      this.ObjectContext.ObjectStateManager.TransactionManager.IsAttachTracking ||
1250                      this.ObjectContext.ObjectStateManager.TransactionManager.IsDetectChanges))
1251                 {
1252                     this.AddToNavigationProperty(wrappedTarget);
1253                     targetRelatedEnd.AddToNavigationProperty(this._wrappedOwner);
1254                 }
1255             }
1256             else if (this.ObjectContext != null || targetRelatedEnd.ObjectContext != null)
1257             {
1258                 // Only one entity has a context, so figure out which one it is, and determine which entity we will be adding to it
1259                 if (this.ObjectContext == null)
1260                 {
1261                     attachedRelatedEnd = targetRelatedEnd;
1262                     entityToAdd = _wrappedOwner;
1263                 }
1264                 else
1265                 {
1266                     attachedRelatedEnd = this;
1267                     entityToAdd = wrappedTarget;
1268                 }
1269
1270                 if (!attachedRelatedEnd.UsingNoTracking)
1271                 {
1272                     TransactionManager transManager = attachedRelatedEnd.WrappedOwner.Context.ObjectStateManager.TransactionManager;
1273                     transManager.BeginAddTracking();
1274
1275                     try
1276                     {
1277                         bool doCleanup = true;
1278
1279                         try
1280                         {
1281                             if (attachedRelatedEnd.WrappedOwner.Context.ObjectStateManager.TransactionManager.TrackProcessedEntities)
1282                             {
1283                                 // The Entity could have been already wrapped by DetectChanges
1284                                 if (!attachedRelatedEnd.WrappedOwner.Context.ObjectStateManager.TransactionManager.WrappedEntities.ContainsKey(entityToAdd.Entity))
1285                                 {
1286                                     attachedRelatedEnd.WrappedOwner.Context.ObjectStateManager.TransactionManager.WrappedEntities.Add(entityToAdd.Entity, entityToAdd);
1287                                 }
1288                                 attachedRelatedEnd.WrappedOwner.Context.ObjectStateManager.TransactionManager.ProcessedEntities.Add(attachedRelatedEnd.WrappedOwner);
1289                             }
1290
1291                             attachedRelatedEnd.AddGraphToObjectStateManager(entityToAdd, relationshipAlreadyExists,
1292                                 addRelationshipAsUnchanged, /*doAttach*/ false);
1293
1294                             // The code below is almost duplicated as the code few lines above 
1295                             // because this one can't be moved outside of try{}catch{}.
1296                             if (entityToAdd.RequiresRelationshipChangeTracking && TargetAccessor.HasProperty)
1297                             {
1298                                 Debug.Assert(this.CheckIfNavigationPropertyContainsEntity(wrappedTarget), "owner's navigation property doesn't contain the target entity as expected");
1299                                 targetRelatedEnd.AddToNavigationProperty(this._wrappedOwner);
1300                             }
1301
1302                             doCleanup = false;
1303                         }
1304                         finally
1305                         {
1306                             if (doCleanup)
1307                             {
1308                                 Debug.Assert(entityToAdd != null, "entityToAdd should be set if attachedRelatedEnd is set");
1309
1310                                 attachedRelatedEnd.WrappedOwner.Context.ObjectStateManager.DegradePromotedRelationships();
1311
1312                                 // Remove the source entity from the target related end
1313                                 attachedRelatedEnd.FixupOtherEndOfRelationshipForRemove(entityToAdd, /*preserveForeignKey*/ false);
1314
1315                                 // Remove the target entity from the source related end
1316                                 attachedRelatedEnd.RemoveFromCache(entityToAdd, /*resetIsLoaded*/ false, /*preserveForeignKey*/ false);
1317
1318                                 // Remove the graph that we just tried to add to the context
1319                                 entityToAdd.RelationshipManager.NodeVisited = true;
1320                                 RelationshipManager.RemoveRelatedEntitiesFromObjectStateManager(entityToAdd);
1321                                 RemoveEntityFromObjectStateManager(entityToAdd);
1322                             }
1323                         }
1324                     }
1325                     finally
1326                     {
1327                         attachedRelatedEnd.WrappedOwner.Context.ObjectStateManager.TransactionManager.EndAddTracking();
1328                     }
1329                 }
1330             }
1331
1332             // FK: update foreign key values on the dependent end.
1333             if (this.ObjectContext != null &&
1334                 this.IsForeignKey &&
1335                 !this.ObjectContext.ObjectStateManager.TransactionManager.IsGraphUpdate)
1336             {
1337                 // Note that we use "forceChange" below so that the FK properties will be set as modified
1338                 // even if they don't actually change.
1339                 if (this.IsDependentEndOfReferentialConstraint(false))
1340                 {
1341                     Debug.Assert(this is EntityReference, "Dependent end cannot be a collection.");
1342                     ((EntityReference)this).UpdateForeignKeyValues(_wrappedOwner, wrappedTarget, changedFKs: null, forceChange: forceForeignKeyChanges);
1343                 }
1344                 else if (targetRelatedEnd.IsDependentEndOfReferentialConstraint(false))
1345                 {
1346                     Debug.Assert(targetRelatedEnd is EntityReference, "Dependent end cannot be a collection.");
1347                     ((EntityReference)targetRelatedEnd).UpdateForeignKeyValues(wrappedTarget, _wrappedOwner, changedFKs: null, forceChange: forceForeignKeyChanges);
1348                 }
1349             }
1350
1351             // else neither entity is associated with a context, so there is no state manager to update
1352             // fire the Association changed event, first on targetRelatedEnd then on this EC
1353             targetRelatedEnd.OnAssociationChanged(CollectionChangeAction.Add, _wrappedOwner.Entity);
1354             OnAssociationChanged(CollectionChangeAction.Add, wrappedTarget.Entity);
1355         }
1356
1357         private void AddGraphToObjectStateManager(IEntityWrapper wrappedEntity, bool relationshipAlreadyExists,
1358                                                   bool addRelationshipAsUnchanged, bool doAttach)
1359         {
1360             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
1361             Debug.Assert(!UsingNoTracking, "Should not be attempting to add graphs to the state manager with NoTracking related ends");
1362
1363             AddEntityToObjectStateManager(wrappedEntity, doAttach);
1364             if (!relationshipAlreadyExists &&
1365                 this.ObjectContext != null && wrappedEntity.Context != null)
1366             {
1367                 if (!this.IsForeignKey)
1368                 {
1369                     AddRelationshipToObjectStateManager(wrappedEntity, addRelationshipAsUnchanged, doAttach);
1370                 }
1371
1372                 if (wrappedEntity.RequiresRelationshipChangeTracking || WrappedOwner.RequiresRelationshipChangeTracking)
1373                 {
1374                     this.UpdateSnapshotOfRelationships(wrappedEntity);
1375                     if (doAttach)
1376                     {
1377                         EntityEntry entry = _context.ObjectStateManager.GetEntityEntry(wrappedEntity.Entity);
1378                         wrappedEntity.RelationshipManager.CheckReferentialConstraintProperties(entry);
1379                     }
1380                 }
1381             }
1382             WalkObjectGraphToIncludeAllRelatedEntities(wrappedEntity, addRelationshipAsUnchanged, doAttach);
1383         }
1384
1385         private void UpdateSnapshotOfRelationships(IEntityWrapper wrappedEntity)
1386         {
1387             RelatedEnd otherRelatedEnd = this.GetOtherEndOfRelationship(wrappedEntity);
1388             if (!otherRelatedEnd.ContainsEntity(this.WrappedOwner))
1389             {
1390                 // Since we now align changes, we can allow the Add to remove the old value
1391                 // Reference/FK violations are detected elsewhere
1392                 otherRelatedEnd.AddToLocalCache(this.WrappedOwner, applyConstraints: false);
1393             }
1394         }
1395
1396         internal void Remove(IEntityWrapper wrappedEntity, bool doFixup, bool deleteEntity, bool deleteOwner, bool applyReferentialConstraints, bool preserveForeignKey)
1397         {
1398             if (wrappedEntity.RequiresRelationshipChangeTracking &&      // Is it POCO?
1399                 doFixup &&                                               // Remove() is called for both ends of relationship, once with doFixup==true, once with doFixup==false. Verify only one time.
1400                 this.TargetAccessor.HasProperty)                     // Is there anything to verify?
1401             {
1402                 bool contains = this.CheckIfNavigationPropertyContainsEntity(wrappedEntity);
1403
1404                 if (!contains)
1405                 {
1406                     RelatedEnd relatedEnd = GetOtherEndOfRelationship(wrappedEntity);
1407                     relatedEnd.RemoveFromNavigationProperty(this.WrappedOwner);
1408                 }
1409             }
1410
1411             if (!this.ContainsEntity(wrappedEntity))
1412             {
1413                 return;
1414             }
1415
1416             // There can be a case when symmetrical Remove() shall be performed because of Referential Constraints
1417             // Example:
1418             //   Relationship Client -> Order with Referential Constraint on in.
1419             //   When user calls (pseudo code) Order.Remove(Client), we perform Client.Remove(Order), 
1420             //   because removing relationship between Client and Order should cause cascade delete on the Order side.
1421             if (null != _context && doFixup &&
1422                 applyReferentialConstraints &&
1423                 IsDependentEndOfReferentialConstraint(false))  // don't check the nullability of the "from" properties
1424             {
1425                 // Remove _wrappedOwner from the related end with applying Referential Constraints
1426                 RelatedEnd relatedEnd = GetOtherEndOfRelationship(wrappedEntity);
1427                 relatedEnd.Remove(_wrappedOwner, doFixup, deleteEntity, deleteOwner, applyReferentialConstraints, preserveForeignKey);
1428
1429                 return;
1430             }
1431
1432
1433             //The following call will verify that the given entity is part of the collection or ref.
1434             bool fireEvent = RemoveFromCache(wrappedEntity, false, preserveForeignKey);
1435
1436             if (!UsingNoTracking &&
1437                 this.ObjectContext != null &&
1438                 !this.IsForeignKey)
1439             {
1440                 MarkRelationshipAsDeletedInObjectStateManager(wrappedEntity, _wrappedOwner, _relationshipSet, _navigation);
1441             }
1442
1443             if (doFixup)
1444             {
1445                 FixupOtherEndOfRelationshipForRemove(wrappedEntity, preserveForeignKey);
1446
1447                 // For the "LocalPublicAPI" just remove the entity from the related end, don't trigger cascade delete
1448                 if (_context == null || !_context.ObjectStateManager.TransactionManager.IsLocalPublicAPI)
1449                 {
1450                     //The related end "entity" cannot live without this side "owner". It should be deleted. Cascade this 
1451                     // effect to related entities of the "related" entity
1452                     // We skip this delete/detach if the entity is being reparented (TransactionManager.EntityBeingReparented)
1453                     // or if the reference is being nulled as part of fixup in a POCO proxy while setting the FK (InFKSetter).
1454                     if (null != _context && (deleteEntity ||
1455                        (deleteOwner && CheckCascadeDeleteFlag(_fromEndProperty)) ||
1456                        (applyReferentialConstraints && IsPrincipalEndOfReferentialConstraint())) &&
1457                         !ReferenceEquals(wrappedEntity.Entity, _context.ObjectStateManager.TransactionManager.EntityBeingReparented) &&
1458                         !ReferenceEquals(_context.ObjectStateManager.EntityInvokingFKSetter, wrappedEntity.Entity))
1459                     {
1460                         //Once related entity is deleted, all relationships involving related entity would be updated
1461
1462                         // RemoveEntityFromRelatedEnds check for graph circularities to make sure
1463                         // it does not get into infinite loop
1464                         EnsureRelationshipNavigationAccessorsInitialized();
1465                         RemoveEntityFromRelatedEnds(wrappedEntity, _wrappedOwner, _navigation.Reverse);
1466                         MarkEntityAsDeletedInObjectStateManager(wrappedEntity);
1467                     }
1468                 }
1469             }
1470
1471             if (fireEvent)
1472             {
1473                 OnAssociationChanged(CollectionChangeAction.Remove, wrappedEntity.Entity);
1474             }
1475         }
1476
1477
1478         /// <summary>
1479         /// Returns true if this Related end represents the dependent of a Referential Constraint
1480         /// </summary>
1481         /// <param name="checkIdentifying">If true then the method will only return true if the Referential Constraint is identifying</param>
1482         internal bool IsDependentEndOfReferentialConstraint(bool checkIdentifying)
1483         {
1484             if (null != _relationMetadata)
1485             {
1486                 // NOTE Referential constraints collection will usually contains 0 or 1 element,
1487                 // so performance shouldn't be an issue here
1488                 foreach (ReferentialConstraint constraint in ((AssociationType)this.RelationMetadata).ReferentialConstraints)
1489                 {
1490                     if (constraint.ToRole == this.FromEndProperty)
1491                     {
1492                         if (checkIdentifying)
1493                         {
1494                             EntityType entityType = constraint.ToRole.GetEntityType();
1495                             bool allPropertiesAreKeyProperties = RelatedEnd.CheckIfAllPropertiesAreKeyProperties(entityType.KeyMemberNames, constraint.ToProperties);
1496
1497                             return allPropertiesAreKeyProperties;
1498                         }
1499                         else
1500                         {
1501                             // Example: 
1502                             //    Client<C_ID> --- Order<O_ID, Client_ID>
1503                             //    RI Constraint: Principal/From <Client.C_ID>,  Dependent/To <Order.Client_ID>
1504                             // When current RelatedEnd is a CollectionOrReference in Order's relationships,
1505                             // constarint.ToRole == this._fromEndProperty == Order
1506                             return true;
1507                         }
1508                     }
1509                 }
1510             }
1511             return false;
1512         }
1513
1514         /// <summary>
1515         /// Check if current RelatedEnd is a Principal end of some Referential Constraint and if some of the "from" properties is not-nullable
1516         /// </summary>
1517         internal bool IsPrincipalEndOfReferentialConstraint()
1518         {
1519             if (null != _relationMetadata)
1520             {
1521                 // NOTE Referential constraints collection will usually contains 0 or 1 element,
1522                 // so performance shouldn't be an issue here
1523                 foreach (ReferentialConstraint constraint in ((AssociationType)_relationMetadata).ReferentialConstraints)
1524                 {
1525                     if (constraint.FromRole == this._fromEndProperty)
1526                     {
1527                         EntityType entityType = constraint.ToRole.GetEntityType();
1528                         bool allPropertiesAreKeyProperties = RelatedEnd.CheckIfAllPropertiesAreKeyProperties(entityType.KeyMemberNames, constraint.ToProperties);
1529
1530                         // Example: 
1531                         //    Client<C_ID> --- Order<O_ID, Client_ID>
1532                         //    RI Constraint: Principal/From <Client.C_ID>,  Dependent/To <Order.Client_ID>
1533                         // When current RelatedEnd is a CollectionOrReference in Client's relationships,
1534                         // constarint.FromRole == this._fromEndProperty == Client
1535                         return allPropertiesAreKeyProperties;
1536                     }
1537                 }
1538             }
1539             return false;
1540         }
1541
1542         static internal bool CheckIfAllPropertiesAreKeyProperties(string[] keyMemberNames, ReadOnlyMetadataCollection<EdmProperty> toProperties)
1543         {
1544             // Check if some of the "to" properties is not a key property
1545             foreach (EdmProperty property in toProperties)
1546             {
1547                 bool found = false;
1548                 foreach (string keyPropertyName in keyMemberNames)
1549                 {
1550                     if (keyPropertyName == property.Name)
1551                     {
1552                         found = true;
1553                         break;
1554                     }
1555                 }
1556                 if (!found)
1557                 {
1558                     return false;
1559                 }
1560             }
1561             return true;
1562         }
1563
1564         //Add given entity and its relationship to ObjectStateManager. Walk graph to recursively
1565         // add all entities in the graph.
1566         // If doAttach==TRUE, the entities are attached directly as Unchanged without calling AcceptChanges()
1567         internal void IncludeEntity(IEntityWrapper wrappedEntity, bool addRelationshipAsUnchanged, bool doAttach)
1568         {
1569             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
1570             Debug.Assert(!UsingNoTracking, "Should not be trying to include entities in the state manager for NoTracking related ends");
1571
1572             //check to see if entity is already added to the cache
1573             //search by object reference so that we will not find any entries with the same key but a different object instance
1574             // NOTE: if (cacheEntry.Entity == entity) then this part of the graph is skipped
1575             EntityEntry cacheEntry = _context.ObjectStateManager.FindEntityEntry(wrappedEntity.Entity);
1576             Debug.Assert(cacheEntry == null || cacheEntry.Entity == wrappedEntity.Entity,
1577                     "Expected to have looked up this state entry by reference, how did we get a different entity?");
1578
1579             if (null != cacheEntry && cacheEntry.State == EntityState.Deleted)
1580             {
1581                 throw EntityUtil.UnableToAddRelationshipWithDeletedEntity();
1582             }
1583
1584             if (wrappedEntity.RequiresRelationshipChangeTracking || WrappedOwner.RequiresRelationshipChangeTracking)
1585             {
1586                 // Verify relationship fixup before including rest of the graph.
1587                 RelatedEnd otherRelatedEnd = GetOtherEndOfRelationship(wrappedEntity);
1588
1589                 // Validate the type is compatible before trying to get/set properties on it.
1590                 // The following will throw if the type is not mapped.
1591                 _context.GetTypeUsage(otherRelatedEnd.WrappedOwner.IdentityType);
1592
1593                 // If the other end is a reference that is non-null, then don't overwrite it.
1594                 // If the reference is non-null and doesn't match what we think it should be, then throw.
1595                 EntityReference otherEndAsRef = otherRelatedEnd as EntityReference;
1596                 if (otherEndAsRef != null)
1597                 {
1598                     if (otherEndAsRef.NavigationPropertyIsNullOrMissing())
1599                     {
1600                         otherRelatedEnd.AddToNavigationProperty(this._wrappedOwner);
1601                         // If the other end is a dependent that is already tracked, then we need to make sure
1602                         // its FK props are marked as modified even though we are not fixing them up.
1603                         Debug.Assert(ObjectContext != null, "Expected attached context at this point.");
1604                         if (cacheEntry != null && 
1605                             ObjectContext.ObjectStateManager.TransactionManager.IsAddTracking &&
1606                             IsForeignKey &&
1607                             otherRelatedEnd.IsDependentEndOfReferentialConstraint(checkIdentifying: false))
1608                         {
1609                             otherRelatedEnd.MarkForeignKeyPropertiesModified();
1610                         }
1611                     }
1612                     else if (!otherEndAsRef.CheckIfNavigationPropertyContainsEntity(this._wrappedOwner))
1613                     {
1614                         throw new InvalidOperationException(System.Data.Entity.Strings.ObjectStateManager_ConflictingChangesOfRelationshipDetected(
1615                             otherEndAsRef.RelationshipNavigation.To,
1616                             otherEndAsRef.RelationshipNavigation.RelationshipName));
1617                     }
1618                 }
1619                 else
1620                 {
1621                     // For collections, always add
1622                     otherRelatedEnd.AddToNavigationProperty(this._wrappedOwner);
1623                 }
1624             }
1625
1626             if (null == cacheEntry)
1627             {
1628                 // NOTE (Attach): if (null == entity.Key) then check must be performed whether entity really
1629                 // doesn't exist in the context (by creating fake Key and calling FindObjectStateEntry(Key) )
1630                 // This is done in the ObjectContext::AttachSingleObject().
1631
1632                 AddGraphToObjectStateManager(wrappedEntity, /*relationshipAlreadyExists*/ false,
1633                                              addRelationshipAsUnchanged, doAttach);
1634             }
1635
1636             // There is a possibility that related entity is added to cache but relationship is not added.
1637             // Example: Suppose A and B are related. When walking the graph it is possible that 
1638             // node B was visited through some relationship other than A-B. 
1639             else if (null == FindRelationshipEntryInObjectStateManager(wrappedEntity))
1640             {
1641                 // If we have a reference with a detached key, make sure the key matches the relationship we are about to add
1642                 EntityReference entityRef = this as EntityReference;
1643                 if (entityRef != null && entityRef.DetachedEntityKey != null)
1644                 {
1645                     EntityKey targetKey = wrappedEntity.EntityKey;
1646                     if (entityRef.DetachedEntityKey != targetKey)
1647                     {
1648                         // Check for the case where a NoTracking (with detached entity key) is being Added and throw the same
1649                         // exception we do elsewhere for this case.
1650                         // We might consider changing this behavior in the future to just put the entity in the Added state,
1651                         // but for consistency for now we throw the same exception as elsewhere.
1652                         if (targetKey.IsTemporary)
1653                         {
1654                             throw EntityUtil.CannotCreateRelationshipBetweenTrackedAndNoTrackedEntities(_navigation.To);
1655                         }
1656
1657                         throw EntityUtil.EntityKeyValueMismatch();
1658                     }
1659                     // else -- null just means the key isn't set, so the target entity key doesn't also have to be null
1660                 }
1661
1662                 if (this.ObjectContext != null && wrappedEntity.Context != null)
1663                 {
1664                     if (!this.IsForeignKey)
1665                     {
1666                         if (cacheEntry.State == EntityState.Added)
1667                         {
1668                             // In POCO, when the graph is partially attached and user is calling Attach on the detached entity
1669                             // and the entity in the context is in the Added state, the relationship has to created also in Added state.
1670                             AddRelationshipToObjectStateManager(wrappedEntity, addRelationshipAsUnchanged, false);
1671                         }
1672                         else
1673                         {
1674                             AddRelationshipToObjectStateManager(wrappedEntity, addRelationshipAsUnchanged, doAttach);
1675                         }
1676                     }
1677
1678                     if (wrappedEntity.RequiresRelationshipChangeTracking || WrappedOwner.RequiresRelationshipChangeTracking)
1679                     {
1680                         this.UpdateSnapshotOfRelationships(wrappedEntity);
1681                         if (doAttach && cacheEntry.State != EntityState.Added)
1682                         {
1683                             EntityEntry entry = this.ObjectContext.ObjectStateManager.GetEntityEntry(wrappedEntity.Entity);
1684                             wrappedEntity.RelationshipManager.CheckReferentialConstraintProperties(entry);
1685                         }
1686                     }
1687                 }
1688             }
1689             // else relationship is already there, nothing more to do
1690         }
1691
1692         internal void MarkForeignKeyPropertiesModified()
1693         {
1694             Debug.Assert(IsForeignKey, "cannot update foreign key values if the relationship is not a FK");
1695             ReferentialConstraint constraint = ((AssociationType)RelationMetadata).ReferentialConstraints[0];
1696             Debug.Assert(constraint != null, "null constraint");
1697
1698             EntityEntry dependentEntry = WrappedOwner.ObjectStateEntry;
1699             Debug.Assert(dependentEntry != null, "Expected tracked entity.");
1700
1701             // No need to try to mark properties as modified for added/deleted/detached entities.
1702             // Even if the entity is modified, the FK props may not be modified.
1703             if (dependentEntry.State == EntityState.Unchanged || dependentEntry.State == EntityState.Modified)
1704             {
1705                 foreach (var dependentProp in constraint.ToProperties)
1706                 {
1707                     dependentEntry.SetModifiedProperty(dependentProp.Name);
1708                 }
1709             }
1710         }
1711
1712         internal abstract bool CheckIfNavigationPropertyContainsEntity(IEntityWrapper wrapper);
1713
1714         internal abstract void VerifyNavigationPropertyForAdd(IEntityWrapper wrapper);
1715
1716         internal void AddToNavigationProperty(IEntityWrapper wrapper)
1717         {
1718             Debug.Assert(this.RelationshipNavigation != null, "null RelationshipNavigation");
1719
1720             if (this.TargetAccessor.HasProperty && !this.CheckIfNavigationPropertyContainsEntity(wrapper))
1721             {
1722                 Debug.Assert(wrapper.Context != null, "Expected context to be available.");
1723                 // We keep track of the nav properties we have set during Add/Attach so that they
1724                 // can be undone during rollback.
1725                 TransactionManager tm = wrapper.Context.ObjectStateManager.TransactionManager;
1726                 if (tm.IsAddTracking || tm.IsAttachTracking)
1727                 {
1728                     wrapper.Context.ObjectStateManager.TrackPromotedRelationship(this, wrapper);
1729                 }
1730                 this.AddToObjectCache(wrapper);
1731             }
1732         }
1733
1734         internal void RemoveFromNavigationProperty(IEntityWrapper wrapper)
1735         {
1736             Debug.Assert(this.RelationshipNavigation != null, "null RelationshipNavigation");
1737
1738             if (this.TargetAccessor.HasProperty && this.CheckIfNavigationPropertyContainsEntity(wrapper))
1739             {
1740                 this.RemoveFromObjectCache(wrapper);
1741             }
1742         }
1743
1744         // Remove given entity and its relationship from ObjectStateManager.
1745         // Traversegraph to recursively remove all entities in the graph.
1746         internal void ExcludeEntity(IEntityWrapper wrappedEntity)
1747         {
1748             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
1749             Debug.Assert(!UsingNoTracking, "Should not try to exclude entities from the state manager for NoTracking related ends.");
1750
1751             if (!_context.ObjectStateManager.TransactionManager.TrackProcessedEntities ||
1752                 !(_context.ObjectStateManager.TransactionManager.IsAttachTracking || _context.ObjectStateManager.TransactionManager.IsAddTracking) ||
1753                 _context.ObjectStateManager.TransactionManager.ProcessedEntities.Contains(wrappedEntity))
1754             {
1755                 //check to see if entity is already removed from the cache
1756                 EntityEntry cacheEntry = _context.ObjectStateManager.FindEntityEntry(wrappedEntity.Entity);
1757
1758                 if (null != cacheEntry && cacheEntry.State != EntityState.Deleted && !wrappedEntity.RelationshipManager.NodeVisited)
1759                 {
1760                     wrappedEntity.RelationshipManager.NodeVisited = true;
1761
1762                     RelationshipManager.RemoveRelatedEntitiesFromObjectStateManager(wrappedEntity);
1763                     if (!this.IsForeignKey)
1764                     {
1765                         RemoveRelationshipFromObjectStateManager(wrappedEntity, _wrappedOwner, _relationshipSet, _navigation);
1766                     }
1767                     RemoveEntityFromObjectStateManager(wrappedEntity);
1768                 }
1769                 // There is a possibility that related entity is removed from cache but relationship is not removed.
1770                 // Example: Suppose A and B are related. When walking the graph it is possible that 
1771                 // node B was visited through some relationship other than A-B. 
1772                 else if (!this.IsForeignKey && null != FindRelationshipEntryInObjectStateManager(wrappedEntity))
1773                 {
1774                     RemoveRelationshipFromObjectStateManager(wrappedEntity, _wrappedOwner, _relationshipSet, _navigation);
1775                 }
1776             }
1777         }
1778
1779         internal RelationshipEntry FindRelationshipEntryInObjectStateManager(IEntityWrapper wrappedEntity)
1780         {
1781             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
1782             Debug.Assert(!UsingNoTracking, "Should not look for RelationshipEntry in ObjectStateManager for NoTracking cases.");
1783             EntityKey entityKey = wrappedEntity.EntityKey;
1784             EntityKey ownerKey = _wrappedOwner.EntityKey;
1785             return this._context.ObjectStateManager.FindRelationship(_relationshipSet,
1786                 new KeyValuePair<string, EntityKey>(_navigation.From, ownerKey),
1787                 new KeyValuePair<string, EntityKey>(_navigation.To, entityKey));
1788         }
1789
1790         internal void Clear(IEntityWrapper wrappedEntity, RelationshipNavigation navigation, bool doCascadeDelete)
1791         {
1792             ClearCollectionOrRef(wrappedEntity, navigation, doCascadeDelete);
1793         }
1794
1795         // Check if related entities contain proper property values 
1796         // (entities with temporary keys are skipped)
1797         internal bool CheckReferentialConstraintProperties(EntityEntry ownerEntry)
1798         {
1799             // if the related end contains a real entity or is a reference with a detached entitykey, we need to check for RI constraints
1800             if (!this.IsEmpty() ||
1801                 ((ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne ||
1802                 ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One) &&
1803                 ((EntityReference)this).DetachedEntityKey != null))
1804             {
1805                 foreach (ReferentialConstraint constraint in ((AssociationType)this.RelationMetadata).ReferentialConstraints)
1806                 {
1807                     // Check properties in principals
1808                     if (constraint.ToRole == FromEndProperty)
1809                     {
1810                         Debug.Assert(this is EntityReference, "Expected reference to principal");
1811                         EntityKey principalKey;
1812                         if (IsEmpty())
1813                         {
1814                             // Generally for foreign keys we want to use the EntityKey to do RI constraint validation
1815                             // However, if we are doing an Add/Attach, we should use the DetachedEntityKey because this is the value
1816                             // set by the user while the entity was detached, and should be used until the entity is fully added/attached
1817                             if (this.IsForeignKey &&
1818                                 !(this.ObjectContext.ObjectStateManager.TransactionManager.IsAddTracking ||
1819                                   this.ObjectContext.ObjectStateManager.TransactionManager.IsAttachTracking))
1820                             {
1821                                 principalKey = ((EntityReference)this).EntityKey;
1822                             }
1823                             else
1824                             {
1825                                 principalKey = ((EntityReference)this).DetachedEntityKey;
1826                             }
1827                         }
1828                         else
1829                         {
1830                             IEntityWrapper wrappedRelatedEntity = ((EntityReference)this).ReferenceValue;
1831                             // For Added entities, it doesn't matter what the key value is since it can't be trusted anyway.
1832                             if (wrappedRelatedEntity.ObjectStateEntry != null && wrappedRelatedEntity.ObjectStateEntry.State == EntityState.Added)
1833                             {
1834                                 return true;
1835                             }
1836                             principalKey = ExtractPrincipalKey(wrappedRelatedEntity);
1837                         }
1838                         if (!VerifyRIConstraintsWithRelatedEntry(constraint, ownerEntry.GetCurrentEntityValue, principalKey))
1839                         {
1840                             return false;
1841                         }
1842                     }
1843                     else if (constraint.FromRole == FromEndProperty)
1844                     {
1845                         if (IsEmpty())
1846                         {
1847                             // related end is empty, so we must have a reference with a detached key
1848                             EntityKey detachedKey = ((EntityReference)this).DetachedEntityKey;
1849 #if DEBUG
1850                             // If the constraint is not PK<->PK then we can't validate it here.
1851                             // This debug code checks that we don't try to validate it.
1852                             List<string> keyNames = new List<string>(from v in detachedKey.EntityKeyValues select v.Key);
1853                             foreach (EdmProperty prop in constraint.ToProperties)
1854                             {
1855                                 Debug.Assert(keyNames.Contains(prop.Name), "Attempt to validate constraint where some FK values are not in the dependent PK");
1856                             }
1857 #endif
1858                             // don't need to validate the principal/detached key here because that has already been done during AttachContext
1859                             if (!VerifyRIConstraintsWithRelatedEntry(constraint, detachedKey.FindValueByName, ownerEntry.EntityKey))
1860                             {
1861                                 return false;
1862                             }
1863                         }
1864                         else
1865                         {
1866                             foreach (IEntityWrapper wrappedRelatedEntity in GetWrappedEntities())
1867                             {
1868                                 EntityEntry dependent = wrappedRelatedEntity.ObjectStateEntry;
1869                                 if (dependent != null &&
1870                                     dependent.State != EntityState.Added &&
1871                                     dependent.State != EntityState.Deleted &&
1872                                     dependent.State != EntityState.Detached)
1873                                 {
1874                                     if (!VerifyRIConstraintsWithRelatedEntry(constraint, dependent.GetCurrentEntityValue, ownerEntry.EntityKey))
1875                                     {
1876                                         return false;
1877                                     }
1878                                 }
1879                             }
1880                         }
1881                     }
1882                     // else keep processing the other constraints
1883                 }
1884             }
1885             return true;
1886         }
1887
1888         private EntityKey ExtractPrincipalKey(IEntityWrapper wrappedRelatedEntity)
1889         {
1890             EntitySet principalEntitySet = GetTargetEntitySetFromRelationshipSet();
1891             // get or create a key to use to compare the values -- the target entity might not have been attached
1892             // yet so it may not have a key, but we can create one here to use for checking the values
1893             EntityKey principalKey = wrappedRelatedEntity.EntityKey;
1894             if (null != (object)principalKey && !principalKey.IsTemporary)
1895             {
1896                 // Validate the key here because we need to get values from it for verification
1897                 // and that will fail if the key is malformed.
1898                 // Verify only if the key already exists.
1899                 EntityUtil.ValidateEntitySetInKey(principalKey, principalEntitySet);
1900                 principalKey.ValidateEntityKey(ObjectContext.MetadataWorkspace, principalEntitySet);
1901             }
1902             else
1903             {
1904                 principalKey = _context.ObjectStateManager.CreateEntityKey(principalEntitySet, wrappedRelatedEntity.Entity);
1905             }
1906             return principalKey;
1907         }
1908
1909         internal static bool VerifyRIConstraintsWithRelatedEntry(ReferentialConstraint constraint, Func<string, object> getDependentPropertyValue, EntityKey principalKey)
1910         {
1911             Debug.Assert(constraint.FromProperties.Count == constraint.ToProperties.Count, "RIC: Referential constraints From/To properties list have different size");
1912
1913             // NOTE order of properties in collections (From/ToProperties) is important.
1914             for (int i = 0; i < constraint.FromProperties.Count; ++i)
1915             {
1916                 string fromPropertyName = constraint.FromProperties[i].Name;
1917                 string toPropertyName = constraint.ToProperties[i].Name;
1918
1919                 object currentValue = principalKey.FindValueByName(fromPropertyName);
1920                 object expectedValue = getDependentPropertyValue(toPropertyName);
1921
1922                 Debug.Assert(currentValue != null, "currentValue is part of Key on an attached entity, it must not be null");
1923
1924                 if (!ByValueEqualityComparer.Default.Equals(currentValue, expectedValue))
1925                 {
1926                     // RI Constraint violated
1927                     return false;
1928                 }
1929             }
1930
1931             return true;
1932         }
1933
1934         public IEnumerator GetEnumerator()
1935         {
1936             // Due to the way the CLR handles IEnumerator return types, the check for a null owner for EntityReferences
1937             // must be made here because GetInternalEnumerator is delay-executed and so will not throw until the 
1938             // enumerator is advanced
1939             if (this is EntityReference)
1940             {
1941                 CheckOwnerNull();
1942             }
1943             DeferredLoad();
1944             return GetInternalEnumerable().GetEnumerator();
1945         }
1946
1947         internal void RemoveAll()
1948         {
1949             //copy into list because changing collection member is not allowed during enumeration.
1950             // If possible avoid copying into list.
1951             List<IEntityWrapper> deletedEntities = null;
1952
1953             bool fireEvent = false;
1954             try
1955             {
1956                 _suppressEvents = true;
1957                 foreach (IEntityWrapper wrappedEntity in GetWrappedEntities())
1958                 {
1959                     if (null == deletedEntities)
1960                     {
1961                         deletedEntities = new List<IEntityWrapper>();
1962                     }
1963                     deletedEntities.Add(wrappedEntity);
1964                 }
1965
1966
1967                 if (fireEvent = (null != deletedEntities) && (deletedEntities.Count > 0))
1968                 {
1969                     foreach (IEntityWrapper wrappedEntity in deletedEntities)
1970                     {
1971                         Remove(wrappedEntity, /*fixup*/true, /*deleteEntity*/false, /*deleteOwner*/true, /*applyReferentialConstraints*/true, /*preserveForeignKey*/false);
1972                     }
1973                 }
1974             }
1975             finally
1976             {
1977                 _suppressEvents = false;
1978             }
1979             if (fireEvent)
1980             {
1981                 OnAssociationChanged(CollectionChangeAction.Refresh, null);
1982             }
1983         }
1984
1985         internal void DetachAll(EntityState ownerEntityState)
1986         {
1987             //copy into list because changing collection member is not allowed during enumeration.
1988             // If possible avoid copying into list.
1989             List<IEntityWrapper> deletedEntities = new List<IEntityWrapper>();
1990
1991             foreach (IEntityWrapper wrappedEntity in GetWrappedEntities())
1992             {
1993                 deletedEntities.Add(wrappedEntity);
1994             }
1995
1996             bool detachRelationship =
1997                     ownerEntityState == EntityState.Added ||
1998                     _fromEndProperty.RelationshipMultiplicity == RelationshipMultiplicity.Many;
1999
2000             // every-fix up will fire with Remove action
2001             // every forward operation (removing from this relatedEnd) will fire with Refresh
2002             // do not merge the loops, handle the related ends separately (when the event is being fired, 
2003             // we should be in good state: for every entity deleted, related event should have been fired)
2004             foreach (IEntityWrapper wrappedEntity in deletedEntities)
2005             {
2006                 // future enhancement: it does not make sense to return in the half way, either remove this code or
2007                 // move it to the right place
2008                 if (!this.ContainsEntity(wrappedEntity))
2009                 {
2010                     return;
2011                 }
2012
2013                 // if this is a reference, set the EntityKey property before removing the relationship and entity
2014                 EntityReference entityReference = this as EntityReference;
2015                 if (entityReference != null)
2016                 {
2017                     entityReference.DetachedEntityKey = entityReference.AttachedEntityKey;
2018                 }
2019
2020                 if (detachRelationship)
2021                 {
2022                     DetachRelationshipFromObjectStateManager(wrappedEntity, _wrappedOwner, _relationshipSet, _navigation);
2023                 }
2024                 RelatedEnd relatedEnd = GetOtherEndOfRelationship(wrappedEntity);
2025                 relatedEnd.RemoveFromCache(_wrappedOwner, /* resetIsLoaded */ true, /*preserveForeignKey*/ false);
2026                 relatedEnd.OnAssociationChanged(CollectionChangeAction.Remove, _wrappedOwner.Entity);
2027             }
2028
2029             // Clear the DetachedEntityKey if this is a foreign key
2030             if (IsForeignKey)
2031             {
2032                 EntityReference entityReference = this as EntityReference;
2033                 if (entityReference != null)
2034                 {
2035                     entityReference.DetachedEntityKey = null;
2036                 }
2037             }
2038
2039             foreach (IEntityWrapper wrappedEntity in deletedEntities)
2040             {
2041                 RelatedEnd relatedEnd = GetOtherEndOfRelationship(wrappedEntity);
2042                 this.RemoveFromCache(wrappedEntity, /* resetIsLoaded */ false, /*preserveForeignKey*/ false);
2043             }
2044             this.OnAssociationChanged(CollectionChangeAction.Refresh, null);
2045
2046             Debug.Assert(this.IsEmpty(), "Collection or reference should be empty");
2047         }
2048
2049         #region Add
2050
2051         internal void AddToCache(IEntityWrapper wrappedEntity, bool applyConstraints)
2052         {
2053             AddToLocalCache(wrappedEntity, applyConstraints);
2054             AddToObjectCache(wrappedEntity);
2055         }
2056         internal abstract void AddToLocalCache(IEntityWrapper wrappedEntity, bool applyConstraints);
2057         internal abstract void AddToObjectCache(IEntityWrapper wrappedEntity);
2058
2059         #endregion
2060
2061         #region Remove
2062
2063         internal bool RemoveFromCache(IEntityWrapper wrappedEntity, bool resetIsLoaded, bool preserveForeignKey)
2064         {
2065             bool result = RemoveFromLocalCache(wrappedEntity, resetIsLoaded, preserveForeignKey);
2066             RemoveFromObjectCache(wrappedEntity);
2067             return result;
2068         }
2069         // Remove from the RelatedEnd
2070         internal abstract bool RemoveFromLocalCache(IEntityWrapper wrappedEntity, bool resetIsLoaded, bool preserveForeignKey);
2071         // Remove from the underlying POCO navigation property
2072         internal abstract bool RemoveFromObjectCache(IEntityWrapper wrappedEntity);
2073
2074         #endregion
2075
2076         internal abstract bool VerifyEntityForAdd(IEntityWrapper wrappedEntity, bool relationshipAlreadyExists);
2077         internal abstract void VerifyType(IEntityWrapper wrappedEntity);
2078         internal abstract bool CanSetEntityType(IEntityWrapper wrappedEntity);
2079         internal abstract void Include(bool addRelationshipAsUnchanged, bool doAttach);
2080         internal abstract void Exclude();
2081         internal abstract void ClearCollectionOrRef(IEntityWrapper wrappedEntity, RelationshipNavigation navigation, bool doCascadeDelete);
2082         internal abstract bool ContainsEntity(IEntityWrapper wrappedEntity);
2083         internal abstract IEnumerable GetInternalEnumerable();
2084         internal abstract IEnumerable<IEntityWrapper> GetWrappedEntities();
2085         internal abstract void RetrieveReferentialConstraintProperties(Dictionary<string, KeyValuePair<object, IntBox>> keyValues, HashSet<object> visited);
2086         internal abstract bool IsEmpty();
2087         internal abstract void OnRelatedEndClear();
2088         internal abstract void ClearWrappedValues();
2089         internal abstract void VerifyMultiplicityConstraintsForAdd(bool applyConstraints);
2090
2091         internal virtual void OnAssociationChanged(CollectionChangeAction collectionChangeAction, object entity)
2092         {
2093             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
2094             if (!_suppressEvents)
2095             {
2096                 if (_onAssociationChanged != null)
2097                 {
2098                     _onAssociationChanged(this, (new CollectionChangeEventArgs(collectionChangeAction, entity)));
2099                 }
2100             }
2101         }
2102
2103         private void AddEntityToObjectStateManager(IEntityWrapper wrappedEntity, bool doAttach)
2104         {
2105             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
2106             Debug.Assert(_context != null, "Can't add to state manager if _context is null");
2107             Debug.Assert(!UsingNoTracking, "Should not add an Entity to ObjectStateManager for NoTracking cases.");
2108
2109             EntitySet es = GetTargetEntitySetFromRelationshipSet();
2110             if (!doAttach)
2111             {
2112                 _context.AddSingleObject(es, wrappedEntity, "entity");
2113             }
2114             else
2115             {
2116                 _context.AttachSingleObject(wrappedEntity, es, "entity");
2117             }
2118
2119             // Now that we know we have a valid EntityKey for the target entity, verify that it matches the detached EntityKey, if there is one
2120             EntityReference entityRef = this as EntityReference;
2121             if (entityRef != null && entityRef.DetachedEntityKey != null)
2122             {
2123                 EntityKey targetKey = wrappedEntity.EntityKey;
2124                 if (entityRef.DetachedEntityKey != targetKey)
2125                 {
2126                     throw EntityUtil.EntityKeyValueMismatch();
2127                 }
2128                 // else -- null just means the key isn't set, so the target entity key doesn't also have to be null
2129             }
2130         }
2131
2132         internal EntitySet GetTargetEntitySetFromRelationshipSet()
2133         {
2134             EntitySet entitySet = null;
2135             AssociationSet associationSet = (AssociationSet)_relationshipSet;
2136             Debug.Assert(associationSet != null, "(AssociationSet) cast failed");
2137
2138             AssociationEndMember associationEndMember = (AssociationEndMember)ToEndMember;
2139             Debug.Assert(associationEndMember != null, "(AssociationEndMember) cast failed");
2140
2141             entitySet = associationSet.AssociationSetEnds[associationEndMember.Name].EntitySet;
2142             Debug.Assert(entitySet != null, "cannot find entitySet");
2143             return entitySet;
2144         }
2145
2146         private RelationshipEntry AddRelationshipToObjectStateManager(IEntityWrapper wrappedEntity, bool addRelationshipAsUnchanged, bool doAttach)
2147         {
2148             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
2149             Debug.Assert(!UsingNoTracking, "Should not add Relationship to ObjectStateManager for NoTracking cases.");
2150             Debug.Assert(!this.IsForeignKey, "for IsForeignKey relationship ObjectStateEntries don't exist");
2151             Debug.Assert(this._context != null && wrappedEntity.Context != null, "should be called only if both entities are attached");
2152             Debug.Assert(this._context == wrappedEntity.Context, "both entities should be attached to the same context");
2153
2154             EntityKey ownerKey = _wrappedOwner.EntityKey;
2155             EntityKey entityKey = wrappedEntity.EntityKey;
2156             EntityUtil.CheckEntityKeyNull(ownerKey);
2157             EntityUtil.CheckEntityKeyNull(entityKey);
2158
2159             return this.ObjectContext.ObjectStateManager.AddRelation(
2160                 new RelationshipWrapper((AssociationSet)_relationshipSet,
2161                        new KeyValuePair<string, EntityKey>(_navigation.From, ownerKey),
2162                        new KeyValuePair<string, EntityKey>(_navigation.To, entityKey)),
2163                 // When Add method is called through Load API the relationship cache entries
2164                 // needs to be added to ObjectStateManager in Unchanged state rather then Added state
2165                 (addRelationshipAsUnchanged || doAttach) ? EntityState.Unchanged : EntityState.Added);
2166         }
2167
2168         private static void WalkObjectGraphToIncludeAllRelatedEntities(IEntityWrapper wrappedEntity,
2169                                                                        bool addRelationshipAsUnchanged, bool doAttach)
2170         {
2171             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
2172             foreach (RelatedEnd relatedEnd in wrappedEntity.RelationshipManager.Relationships)
2173             {
2174                 relatedEnd.Include(addRelationshipAsUnchanged, doAttach);
2175             }
2176         }
2177
2178         internal static void RemoveEntityFromObjectStateManager(IEntityWrapper wrappedEntity)
2179         {
2180             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
2181             EntityEntry entry;
2182
2183             if (wrappedEntity.Context != null &&
2184                 wrappedEntity.Context.ObjectStateManager.TransactionManager.IsAttachTracking &&
2185                 wrappedEntity.Context.ObjectStateManager.TransactionManager.PromotedKeyEntries.TryGetValue(wrappedEntity.Entity, out entry))
2186             {
2187                 // This is executed only in the cleanup code from ObjectContext.AttachTo()
2188                 // If the entry was promoted in AttachTo(), it has to be degraded now instead of being deleted.
2189                 entry.DegradeEntry();
2190             }
2191             else
2192             {
2193                 entry = MarkEntityAsDeletedInObjectStateManager(wrappedEntity);
2194                 if (entry != null && entry.State != EntityState.Detached)
2195                 {
2196                     entry.AcceptChanges();
2197                 }
2198             }
2199         }
2200
2201         private static void RemoveRelationshipFromObjectStateManager(IEntityWrapper wrappedEntity, IEntityWrapper wrappedOwner, RelationshipSet relationshipSet, RelationshipNavigation navigation)
2202         {
2203             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
2204             Debug.Assert(relationshipSet == null || !(relationshipSet.ElementType as AssociationType).IsForeignKey, "for IsForeignKey relationships ObjectStateEntries don't exist");
2205
2206             RelationshipEntry deletedEntry = MarkRelationshipAsDeletedInObjectStateManager(wrappedEntity, wrappedOwner, relationshipSet, navigation);
2207             if (deletedEntry != null && deletedEntry.State != EntityState.Detached)
2208             {
2209                 deletedEntry.AcceptChanges();
2210             }
2211         }
2212
2213         private void FixupOtherEndOfRelationshipForRemove(IEntityWrapper wrappedEntity, bool preserveForeignKey)
2214         {
2215             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
2216             RelatedEnd relatedEnd = GetOtherEndOfRelationship(wrappedEntity);
2217             relatedEnd.Remove(_wrappedOwner, /*fixup*/false, /*deleteEntity*/false, /*deleteOwner*/false, /*applyReferentialConstraints*/false, preserveForeignKey);
2218             relatedEnd.RemoveFromNavigationProperty(_wrappedOwner);
2219         }
2220
2221         private static EntityEntry MarkEntityAsDeletedInObjectStateManager(IEntityWrapper wrappedEntity)
2222         {
2223             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
2224             EntityEntry entry = null;
2225             if (wrappedEntity.Context != null)
2226             {
2227                 entry = wrappedEntity.Context.ObjectStateManager.FindEntityEntry(wrappedEntity.Entity);
2228
2229                 if (entry != null)
2230                 {
2231                     entry.Delete(/*doFixup*/false);
2232                 }
2233             }
2234             return entry;
2235         }
2236
2237         private static RelationshipEntry MarkRelationshipAsDeletedInObjectStateManager(IEntityWrapper wrappedEntity, IEntityWrapper wrappedOwner, RelationshipSet relationshipSet, RelationshipNavigation navigation)
2238         {
2239             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
2240             Debug.Assert(relationshipSet == null || !(relationshipSet.ElementType as AssociationType).IsForeignKey, "for IsForeignKey relationships ObjectStateEntries don't exist");
2241             RelationshipEntry entry = null;
2242             if (wrappedOwner.Context != null && wrappedEntity.Context != null && relationshipSet != null)
2243             {
2244                 EntityKey ownerKey = wrappedOwner.EntityKey;
2245                 EntityKey entityKey = wrappedEntity.EntityKey;
2246
2247                 entry = wrappedEntity.Context.ObjectStateManager.DeleteRelationship(relationshipSet,
2248                                       new KeyValuePair<string, EntityKey>(navigation.From, ownerKey),
2249                                       new KeyValuePair<string, EntityKey>(navigation.To, entityKey));
2250             }
2251             return entry;
2252         }
2253
2254         private static void DetachRelationshipFromObjectStateManager(IEntityWrapper wrappedEntity, IEntityWrapper wrappedOwner, RelationshipSet relationshipSet, RelationshipNavigation navigation)
2255         {
2256             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
2257             if (wrappedOwner.Context != null && wrappedEntity.Context != null && relationshipSet != null)
2258             {
2259                 EntityKey ownerKey = wrappedOwner.EntityKey;
2260                 EntityKey entityKey = wrappedEntity.EntityKey;
2261                 RelationshipEntry entry = wrappedEntity.Context.ObjectStateManager.FindRelationship(relationshipSet,
2262                                                                           new KeyValuePair<string, EntityKey>(navigation.From, ownerKey),
2263                                                                           new KeyValuePair<string, EntityKey>(navigation.To, entityKey));
2264                 if (entry != null)
2265                 {
2266                     entry.DetachRelationshipEntry();
2267                 }
2268             }
2269         }
2270
2271         private static void RemoveEntityFromRelatedEnds(IEntityWrapper wrappedEntity1, IEntityWrapper wrappedEntity2, RelationshipNavigation navigation)
2272         {
2273             Debug.Assert(wrappedEntity1 != null, "IEntityWrapper instance is null.");
2274             Debug.Assert(wrappedEntity2 != null, "IEntityWrapper instance is null.");
2275             foreach (RelatedEnd relatedEnd in wrappedEntity1.RelationshipManager.Relationships)
2276             {
2277                 bool doCascadeDelete = false;
2278                 //check for cascade delete flag
2279                 doCascadeDelete = CheckCascadeDeleteFlag(relatedEnd.FromEndProperty) || relatedEnd.IsPrincipalEndOfReferentialConstraint();
2280                 //Remove the owner from the related end
2281                 relatedEnd.Clear(wrappedEntity2, navigation, doCascadeDelete);
2282             }
2283         }
2284
2285         private static bool CheckCascadeDeleteFlag(RelationshipEndMember relationEndProperty)
2286         {
2287             if (null != relationEndProperty)
2288             {
2289                 return (relationEndProperty.DeleteBehavior == OperationAction.Cascade);
2290             }
2291             return false;
2292         }
2293
2294
2295         internal void AttachContext(ObjectContext context, MergeOption mergeOption)
2296         {
2297             if (!_wrappedOwner.InitializingProxyRelatedEnds)
2298             {
2299                 EntityKey ownerKey = _wrappedOwner.EntityKey;
2300                 EntityUtil.CheckEntityKeyNull(ownerKey);
2301                 EntitySet entitySet = ownerKey.GetEntitySet(context.MetadataWorkspace);
2302
2303                 AttachContext(context, entitySet, mergeOption);
2304             }
2305         }
2306
2307
2308         /// <summary>
2309         /// Set the context and load options so that Query can be constructed on demand.
2310         /// </summary>
2311         internal void AttachContext(ObjectContext context, EntitySet entitySet, MergeOption mergeOption)
2312         {
2313             EntityUtil.CheckArgumentNull(context, "context");
2314             EntityUtil.CheckArgumentMergeOption(mergeOption);
2315             EntityUtil.CheckArgumentNull(entitySet, "entitySet");
2316
2317             _wrappedOwner.RelationshipManager.NodeVisited = false;
2318             // If the context is the same as what we already have, and the mergeOption is consistent with our UsingNoTracking setting, nothing more to do
2319             if (_context == context && (_usingNoTracking == (mergeOption == MergeOption.NoTracking)))
2320             {
2321                 return;
2322             }
2323
2324             bool doCleanup = true;
2325
2326             try
2327             {
2328                 // if the source isn't null, clear it
2329                 _sourceQuery = null;
2330                 this._context = context;
2331                 this._usingNoTracking = (mergeOption == MergeOption.NoTracking);
2332
2333                 EdmType relationshipType;
2334                 RelationshipSet relationshipSet;
2335                 FindRelationshipSet(_context, entitySet, out relationshipType, out relationshipSet);
2336
2337                 if (relationshipSet != null)
2338                 {
2339                     this._relationshipSet = relationshipSet;
2340                     this._relationMetadata = (RelationshipType)relationshipType;
2341                 }
2342                 else
2343                 {
2344                     foreach (EntitySetBase set in entitySet.EntityContainer.BaseEntitySets)
2345                     {
2346                         AssociationSet associationset = set as AssociationSet;
2347                         if (associationset != null)
2348                         {
2349                             if (associationset.ElementType == relationshipType &&
2350                                 associationset.AssociationSetEnds[_navigation.From].EntitySet != entitySet &&
2351                                 associationset.AssociationSetEnds[_navigation.From].EntitySet.ElementType == entitySet.ElementType)
2352                                 throw EntityUtil.EntitySetIsNotValidForRelationship(entitySet.EntityContainer.Name, entitySet.Name, _navigation.From, ((AssociationSet)set).EntityContainer.Name, ((AssociationSet)set).Name);
2353                         }
2354                     }
2355                     throw EntityUtil.NoRelationshipSetMatched(_navigation.RelationshipName);
2356                 }
2357
2358                 //find relation end property
2359                 bool foundFromRelationEnd = false;
2360                 bool foundToRelationEnd = false;
2361                 foreach (AssociationEndMember relationEnd in ((AssociationType)_relationMetadata).AssociationEndMembers) //Only Association relationship is supported
2362                 {
2363                     if (relationEnd.Name == this._navigation.From)
2364                     {
2365                         Debug.Assert(!foundFromRelationEnd, "More than one related end was found with the same role name.");
2366
2367                         foundFromRelationEnd = true;
2368                         this._fromEndProperty = relationEnd;
2369                     }
2370                     if (relationEnd.Name == this._navigation.To)
2371                     {
2372                         Debug.Assert(!foundToRelationEnd, "More than one related end was found with the same role name.");
2373
2374                         foundToRelationEnd = true;
2375                         this._toEndProperty = relationEnd;
2376                     }
2377                 }
2378                 if (!(foundFromRelationEnd && foundToRelationEnd))
2379                 {
2380                     throw EntityUtil.RelatedEndNotFound();
2381                 }
2382
2383                 // If this is a stub EntityReference and the DetachedEntityKey is set, make sure it is valid
2384                 if (this.IsEmpty())
2385                 {
2386                     // if there are no contained entities but this is a reference with a detached entity key, validate the key
2387                     EntityReference entityRef = this as EntityReference;
2388                     if (entityRef != null && entityRef.DetachedEntityKey != null)
2389                     {
2390                         EntityKey detachedKey = entityRef.DetachedEntityKey;
2391                         if (!IsValidEntityKeyType(detachedKey))
2392                         {
2393                             // devnote: We have to check this here instead of in the EntityKey property setter,
2394                             //          because the key could be set to an invalid type temporarily during deserialization
2395                             throw EntityUtil.CannotSetSpecialKeys();
2396                         }
2397                         EntitySet targetEntitySet = detachedKey.GetEntitySet(context.MetadataWorkspace);
2398                         CheckRelationEntitySet(targetEntitySet);
2399                         detachedKey.ValidateEntityKey(ObjectContext.MetadataWorkspace, targetEntitySet);
2400                     }
2401                 }
2402                 // else even for a reference we don't need to validate the key
2403                 // because it will be checked later once we have the key for the contained entity
2404
2405                 doCleanup = false;
2406             }
2407             finally
2408             {
2409                 if (doCleanup)
2410                 {
2411                     // Uninitialize fields, so the cleanup code (for example in RelationshipWrapper.RemoveRelatedEntitiesFromObjectStateManager) 
2412                     // knows that this RelatedEnd was not properly Attached.
2413                     this.DetachContext();
2414                 }
2415             }
2416         }
2417
2418         internal void FindRelationshipSet(ObjectContext context, EntitySet entitySet, out EdmType relationshipType, out RelationshipSet relationshipSet)
2419         {
2420             // find the relationship set
2421             Debug.Assert(context.MetadataWorkspace != null, "The context should not have a null metadata workspace.");
2422
2423             // find the TypeMetadata for the given relationship
2424             relationshipType = context.MetadataWorkspace.GetItem<EdmType>(_navigation.RelationshipName, DataSpace.CSpace);
2425             if (relationshipType == null)
2426             {
2427                 throw EntityUtil.NoRelationshipSetMatched(_navigation.RelationshipName);
2428             }
2429
2430             // find the RelationshipSet
2431             foreach (EntitySetBase entitySetBase in entitySet.EntityContainer.BaseEntitySets)
2432             {
2433                 if ((EdmType)entitySetBase.ElementType == relationshipType)
2434                 {
2435                     if (((AssociationSet)entitySetBase).AssociationSetEnds[_navigation.From].EntitySet == entitySet)
2436                     {
2437                         relationshipSet = (RelationshipSet)entitySetBase;
2438                         return;
2439                     }
2440                 }
2441             }
2442             relationshipSet = null;
2443         }
2444
2445         /// <summary>
2446         /// Clear the source and context.
2447         /// </summary>        
2448         internal void DetachContext()
2449         {
2450             if (this._context != null &&
2451                 this.ObjectContext.ObjectStateManager.TransactionManager.IsAttachTracking &&
2452                 this.ObjectContext.ObjectStateManager.TransactionManager.OriginalMergeOption == MergeOption.NoTracking)
2453             {
2454                 this._usingNoTracking = true;
2455                 return;
2456             }
2457
2458             this._sourceQuery = null;
2459             this._context = null;
2460             this._relationshipSet = null;
2461             this._fromEndProperty = null;
2462             this._toEndProperty = null;
2463             this._relationMetadata = null;
2464
2465             // Detached entity should have IsLoaded property set to false
2466             this._isLoaded = false;
2467         }
2468
2469         /// <summary>
2470         ///  This method is very similar to the private method in Query of U class
2471         ///  Only difference is that it calls meterializer with a overload which does
2472         ///  not skip the deleted items.
2473         /// </summary>
2474         /// <param name="query">query of U</param>
2475         /// <returns></returns>
2476         internal static IEnumerable<U> GetResults<U>(ObjectQuery<U> query)
2477         {
2478             return query.Execute(query.MergeOption);
2479         }
2480
2481         internal RelatedEnd GetOtherEndOfRelationship(IEntityWrapper wrappedEntity)
2482         {
2483             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
2484             EnsureRelationshipNavigationAccessorsInitialized();
2485             return (RelatedEnd)wrappedEntity.RelationshipManager.GetRelatedEnd(_navigation.Reverse, _relationshipFixer);
2486         }
2487
2488         // We have to allow a default constructor for serialization, so we need to make sure that the only
2489         // thing you can do with a null owner is get/set the EntityReference.EntityKey property. All other
2490         // operations are invalid. This needs to be used on all public methods in this class and EntityReference
2491         // but not in EntityCollection because EntityCollection does not have a default constructor.
2492         // It is not possible to get an EntityReference with a null Owner into the RelationshipManager, and there 
2493         // is no way to access EntityReference without creating one using the default constructor or going through
2494         // the RelationshipManager, so we don't need to check this in internal or private methods.
2495         internal void CheckOwnerNull()
2496         {
2497             if (_wrappedOwner.Entity == null)
2498             {
2499                 throw EntityUtil.OwnerIsNull();
2500             }
2501         }
2502
2503         // This method is intended to be used to support the public API InitializeRelatedReference, where we have to take an existing EntityReference
2504         // and set up the appropriate fields as shown below, instead of creating a new EntityReference and setting these fields in the constructor.   
2505         // This is also used by the constructor -- if we add something that needs to be set at construction time, it probably needs to be set for InitializeRelatedReference as well.
2506         internal void InitializeRelatedEnd(IEntityWrapper wrappedOwner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer)
2507         {
2508             SetWrappedOwner(wrappedOwner);
2509             _navigation = navigation;
2510             _relationshipFixer = relationshipFixer;
2511         }
2512
2513         internal void SetWrappedOwner(IEntityWrapper wrappedOwner)
2514         {
2515             _wrappedOwner = wrappedOwner != null ? wrappedOwner : EntityWrapperFactory.NullWrapper;
2516 #pragma warning disable 612 // Disable "obsolete" warning for the _owner field. Used for backwards compatibility.
2517             _owner = wrappedOwner.Entity as IEntityWithRelationships;
2518 #pragma warning restore 612
2519         }
2520
2521         internal static bool IsValidEntityKeyType(EntityKey entityKey)
2522         {
2523             return !(entityKey.IsTemporary ||
2524                      Object.ReferenceEquals(EntityKey.EntityNotValidKey, entityKey) ||
2525                      Object.ReferenceEquals(EntityKey.NoEntitySetKey, entityKey));
2526         }
2527
2528         // This method is required to maintain compatibility with the v1 binary serialization format. 
2529         // In particular, it recreates a entity wrapper from the serialized owner.
2530         // Note that this is only expected to work for non-POCO entities, since serialization of POCO
2531         // entities will not result in serialization of the RelationshipManager or its related objects.
2532         [OnDeserialized()]
2533         [Browsable(false)]
2534         [EditorBrowsable(EditorBrowsableState.Never)]
2535         public void OnDeserialized(StreamingContext context)
2536         {
2537 #pragma warning disable 612 // Disable "obsolete" warning for the _owner field. Used for backwards compatibility.
2538             _wrappedOwner = EntityWrapperFactory.WrapEntityUsingContext(_owner, ObjectContext);
2539 #pragma warning restore 612
2540         }
2541
2542         [NonSerialized]
2543         private NavigationProperty navigationPropertyCache = null;
2544
2545         internal NavigationProperty NavigationProperty
2546         {
2547             get
2548             {
2549                 if (this.navigationPropertyCache == null && _wrappedOwner.Context != null && this.TargetAccessor.HasProperty)
2550                 {
2551                     string navigationPropertyName = this.TargetAccessor.PropertyName;
2552
2553                     EntityType entityType = _wrappedOwner.Context.MetadataWorkspace.GetItem<EntityType>(_wrappedOwner.IdentityType.FullName, DataSpace.OSpace);
2554                     NavigationProperty member;
2555                     if (!entityType.NavigationProperties.TryGetValue(navigationPropertyName, false, out member))
2556                     {
2557                         throw new InvalidOperationException(System.Data.Entity.Strings.RelationshipManager_NavigationPropertyNotFound(navigationPropertyName));
2558                     }
2559                     // Avoid metadata lookups by caching the navigation property locally
2560                     this.navigationPropertyCache = member;
2561                 }
2562                 return this.navigationPropertyCache;
2563             }
2564         }
2565
2566         #region POCO Navigation Property Accessors
2567
2568         internal NavigationPropertyAccessor TargetAccessor
2569         {
2570             get
2571             {
2572                 if (_wrappedOwner.Entity != null)
2573                 {
2574                     EnsureRelationshipNavigationAccessorsInitialized();
2575                     return RelationshipNavigation.ToPropertyAccessor;
2576                 }
2577                 else
2578                 {
2579                     // Disconnected RelatedEnds have no POCO navigation properties
2580                     return NavigationPropertyAccessor.NoNavigationProperty;
2581                 }
2582             }
2583         }
2584
2585         // If the RelationshipNavigation has not been fully initialized, it means this RelatedEnd was created without metadata
2586         // This can occur in serialization scenarios
2587         // Try to look up the metadata in all metadata repositories that are available and populate it
2588         // This must be called before accessing any of the Accessor properties on the RelationshipNavigation
2589         private void EnsureRelationshipNavigationAccessorsInitialized()
2590         {
2591             Debug.Assert(_navigation != null, "Null RelationshipNavigation");
2592             Debug.Assert(_wrappedOwner.Entity != null, "Must be connected to lookup metadata");
2593             if (!RelationshipNavigation.IsInitialized)
2594             {
2595                 NavigationPropertyAccessor sourceAccessor = null;
2596                 NavigationPropertyAccessor targetAccessor = null;
2597
2598                 AssociationType associationType = this.RelationMetadata as AssociationType;
2599                 string relationshipName = _navigation.RelationshipName;
2600                 string sourceRoleName = _navigation.From;
2601                 string targetRoleName = _navigation.To;
2602                 if (associationType != null ||
2603                     RelationshipManager.TryGetRelationshipType(WrappedOwner, WrappedOwner.IdentityType, relationshipName, out associationType) ||
2604                     EntityProxyFactory.TryGetAssociationTypeFromProxyInfo(WrappedOwner, relationshipName, targetRoleName, out associationType))
2605                 {
2606                     AssociationEndMember sourceEnd;
2607                     if (associationType.AssociationEndMembers.TryGetValue(sourceRoleName, false, out sourceEnd))
2608                     {
2609                         EntityType sourceEntityType = MetadataHelper.GetEntityTypeForEnd(sourceEnd);
2610                         targetAccessor = MetadataHelper.GetNavigationPropertyAccessor(sourceEntityType, relationshipName, sourceRoleName, targetRoleName);
2611                     }
2612
2613                     AssociationEndMember targetEnd;
2614                     if (associationType.AssociationEndMembers.TryGetValue(targetRoleName, false, out targetEnd))
2615                     {
2616                         EntityType targetEntityType = MetadataHelper.GetEntityTypeForEnd(targetEnd);
2617                         sourceAccessor = MetadataHelper.GetNavigationPropertyAccessor(targetEntityType, relationshipName, targetRoleName, sourceRoleName);
2618                     }
2619                 }
2620
2621                 if (sourceAccessor == null || targetAccessor == null)
2622                 {
2623                     throw RelationshipManager.UnableToGetMetadata(WrappedOwner, relationshipName);
2624                 }
2625
2626                 RelationshipNavigation.InitializeAccessors(sourceAccessor, targetAccessor);
2627             }
2628         }
2629
2630         #endregion
2631     }
2632 }