Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Objects / DataClasses / EntityReference_TResultType.cs
1 //---------------------------------------------------------------------
2 // <copyright file="EntityReference_TResultType.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       [....]
7 // @backupOwner [....]
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;
16     using System.Data.Metadata.Edm;
17     using System.Data.Objects.Internal;
18     using System.Diagnostics;
19     using System.Runtime.Serialization;
20
21     /// <summary>
22     /// Models a relationship end with multiplicity 1.
23     /// </summary>
24     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
25     [DataContract]
26     [Serializable]
27     public sealed class EntityReference<TEntity> : EntityReference
28         where TEntity : class
29     {
30         // ------
31         // Fields
32         // ------
33
34         // The following fields are serialized.  Adding or removing a serialized field is considered
35         // a breaking change.  This includes changing the field type or field name of existing
36         // serialized fields. If you need to make this kind of change, it may be possible, but it
37         // will require some custom serialization/deserialization code.
38         // Note that this field should no longer be used directly.  Instead, use the _wrappedCachedValue
39         // field.  This field is retained only for compatibility with the serialization format introduced in v1.
40         private TEntity _cachedValue;
41
42         [NonSerialized]
43         private IEntityWrapper _wrappedCachedValue;
44
45         // ------------
46         // Constructors
47         // ------------
48
49         /// <summary>
50         /// The default constructor is required for some serialization scenarios. It should not be used to 
51         /// create new EntityReferences. Use the GetRelatedReference or GetRelatedEnd methods on the RelationshipManager
52         /// class instead.
53         /// </summary>
54         public EntityReference()
55         {
56             _wrappedCachedValue = EntityWrapperFactory.NullWrapper;
57         }
58
59         internal EntityReference(IEntityWrapper wrappedOwner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer)
60             : base(wrappedOwner, navigation, relationshipFixer)
61         {
62             _wrappedCachedValue = EntityWrapperFactory.NullWrapper;
63         }
64
65         // ----------
66         // Properties
67         // ----------
68
69         /// <summary>
70         /// Stub only please replace with actual implementation
71         /// </summary>
72         [System.Xml.Serialization.SoapIgnore]
73         [System.Xml.Serialization.XmlIgnore]
74         public TEntity Value
75         {
76             get
77             {
78                 DeferredLoad();
79                 return (TEntity)ReferenceValue.Entity;
80             }
81             set
82             {
83                 ReferenceValue = EntityWrapperFactory.WrapEntityUsingContext(value, ObjectContext);
84             }
85         }
86
87         internal override IEntityWrapper CachedValue
88         {
89             get { return _wrappedCachedValue; }
90         }
91
92         internal override IEntityWrapper ReferenceValue
93         {
94             get
95             {
96                 CheckOwnerNull();
97                 return _wrappedCachedValue;
98             }
99             set
100             {
101                 CheckOwnerNull();
102                 //setting to same value is a no-op (SQL BU DT # 446320)
103                 //setting to null is a special case because then we will also clear out any Added/Unchanged relationships with key entries, so we can't no-op if Value is null
104                 if (value.Entity != null && value.Entity == _wrappedCachedValue.Entity)
105                 {
106                     return;
107                 }
108
109                 if (null != value.Entity)
110                 {
111                     // Note that this is only done for the case where we are not setting the ref to null because
112                     // clearing a ref is okay--it will cause the dependent to become deleted/detached.
113                     ValidateOwnerWithRIConstraints(value, value == EntityWrapperFactory.NullWrapper ? null : value.EntityKey, checkBothEnds: true);
114                     ObjectContext context = ObjectContext ?? value.Context;
115                     if (context != null)
116                     {
117                         context.ObjectStateManager.TransactionManager.EntityBeingReparented = GetDependentEndOfReferentialConstraint(value.Entity);
118                     }
119                     try
120                     {
121                         Add(value, /*applyConstraints*/false);
122                     }
123                     finally
124                     {
125                         if (context != null)
126                         {
127                             context.ObjectStateManager.TransactionManager.EntityBeingReparented = null;
128                         }
129                     }
130                 }
131                 else
132                 {
133                     if (UsingNoTracking)
134                     {
135                         if (_wrappedCachedValue.Entity != null)
136                         {
137                             // The other end of relationship can be the EntityReference or EntityCollection
138                             // If the other end is EntityReference, its IsLoaded property should be set to FALSE
139                             RelatedEnd relatedEnd = GetOtherEndOfRelationship(_wrappedCachedValue);
140                             relatedEnd.OnRelatedEndClear();
141                         }
142
143                         _isLoaded = false;
144                     }
145                     else
146                     {
147                         if (ObjectContext != null && ObjectContext.ContextOptions.UseConsistentNullReferenceBehavior)
148                         {
149                             AttemptToNullFKsOnRefOrKeySetToNull();
150                         }
151                     }
152
153                     ClearCollectionOrRef(null, null, false);
154                 }
155             }
156         }
157
158         // -------
159         // Methods
160         // -------
161
162         /// <summary>
163         /// Loads the related entity or entities into the local related end using the supplied MergeOption.        
164         /// </summary>        
165         public override void Load(MergeOption mergeOption)
166         {
167             CheckOwnerNull();
168
169             // Validate that the Load is possible
170             bool hasResults;
171             ObjectQuery<TEntity> sourceQuery = ValidateLoad<TEntity>(mergeOption, "EntityReference", out hasResults);
172
173             _suppressEvents = true; // we do not want any event during the bulk operation
174             try
175             {
176                 List<TEntity> refreshedValue = null;
177                 if (hasResults)
178                 {
179                     // Only issue a query if we know it can produce results (in the case of FK, there may not be any 
180                     // results).
181                     refreshedValue = new List<TEntity>(GetResults<TEntity>(sourceQuery));
182                 }
183                 if (null == refreshedValue || refreshedValue.Count == 0)
184                 {
185                     if (!((AssociationType)base.RelationMetadata).IsForeignKey && ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One)
186                     {
187                         //query returned zero related end; one related end was expected.
188                         throw EntityUtil.LessThanExpectedRelatedEntitiesFound();
189                     }
190                     else if (mergeOption == MergeOption.OverwriteChanges || mergeOption == MergeOption.PreserveChanges)
191                     {
192                         // This entity is not related to anything in this AssociationSet and Role on the server.
193                         // If there is an existing _cachedValue, we may need to clear it out, based on the MergeOption
194                         EntityKey sourceKey = WrappedOwner.EntityKey;
195                         EntityUtil.CheckEntityKeyNull(sourceKey);
196                         ObjectStateManager.RemoveRelationships(ObjectContext, mergeOption, (AssociationSet)RelationshipSet, sourceKey, (AssociationEndMember)FromEndProperty);
197                     }
198                     // else this is NoTracking or AppendOnly, and no entity was retrieved by the Load, so there's nothing extra to do
199
200                     // Since we have no value and are not doing a merge, the last step is to set IsLoaded to true
201                     _isLoaded = true;
202                 }
203                 else if (refreshedValue.Count == 1)
204                 {
205                     Merge<TEntity>(refreshedValue, mergeOption, true /*setIsLoaded*/);
206                 }
207                 else
208                 {
209                     // More than 1 result, which is non-recoverable data inconsistency
210                     throw EntityUtil.MoreThanExpectedRelatedEntitiesFound();
211                 }
212             }
213             finally
214             {
215                 _suppressEvents = false;
216             }
217             // fire the AssociationChange with Refresh
218             OnAssociationChanged(CollectionChangeAction.Refresh, null);
219         }
220
221         /// <summary>
222         /// This operation is not allowed if the owner is null
223         /// </summary>
224         /// <returns></returns>
225         internal override IEnumerable GetInternalEnumerable()
226         {
227             if (ReferenceValue.Entity != null)
228             {
229                 yield return (TEntity)ReferenceValue.Entity;
230             }
231         }
232
233         internal override IEnumerable<IEntityWrapper> GetWrappedEntities()
234         {
235             // 
236             return _wrappedCachedValue.Entity == null ? new IEntityWrapper[0] : new IEntityWrapper[] { _wrappedCachedValue };
237         }
238
239         /// <summary>
240         /// Attaches an entity to the EntityReference. The given
241         /// entity is not assumed to be the complete set of related entities.
242         /// 
243         /// Owner and all entities passed in must be in Unchanged or Modified state. 
244         /// Deleted elements are allowed only when the state manager is already tracking the relationship
245         /// instance.
246         /// </summary>
247         /// <param name="entity">The entity to attach to the EntityCollection</param>
248         /// <exception cref="ArgumentNullException">Thrown when <paramref name="entity"/> is null.</exception>
249         /// <exception cref="InvalidOperationException">Thrown when the entity cannot be related via the current relationship end.</exception>
250         public void Attach(TEntity entity)
251         {
252             CheckOwnerNull();
253             EntityUtil.CheckArgumentNull(entity, "entity");
254             Attach(new IEntityWrapper[] { EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext) }, false);
255         }
256
257         internal override void Include(bool addRelationshipAsUnchanged, bool doAttach)
258         {
259             Debug.Assert(this.ObjectContext != null, "Should not be trying to add entities to state manager if context is null");
260
261             // If we have an actual value or a key for this reference, add it to the context
262             if (null != _wrappedCachedValue.Entity)
263             {
264                 // Sometimes with mixed POCO and IPOCO, you can get different instances of IEntityWrappers stored in the IPOCO related ends
265                 // These should be replaced by the IEntityWrapper that is stored in the context
266                 IEntityWrapper identityWrapper = EntityWrapperFactory.WrapEntityUsingContext(_wrappedCachedValue.Entity, WrappedOwner.Context);
267                 if (identityWrapper != _wrappedCachedValue)
268                 {
269                     _wrappedCachedValue = identityWrapper;
270                 }
271                 IncludeEntity(_wrappedCachedValue, addRelationshipAsUnchanged, doAttach);
272             }
273             else if (DetachedEntityKey != null)
274             {
275                 IncludeEntityKey(doAttach);
276             }
277             // else there is nothing to add for this relationship
278         }
279
280         private void IncludeEntityKey(bool doAttach)
281         {
282             ObjectStateManager manager = this.ObjectContext.ObjectStateManager;
283
284             bool addNewRelationship = false;
285             bool addKeyEntry = false;
286             EntityEntry existingEntry = manager.FindEntityEntry(DetachedEntityKey);
287             if (existingEntry == null)
288             {
289                 // add new key entry and create a relationship with it                
290                 addKeyEntry = true;
291                 addNewRelationship = true;
292             }
293             else
294             {
295                 if (existingEntry.IsKeyEntry)
296                 {
297                     // We have an existing key entry, so just need to add a relationship with it
298
299                     // We know the target end of this relationship is 1..1 or 0..1 since it is a reference, so if the source end is also not Many, we have a 1-to-1
300                     if (FromEndProperty.RelationshipMultiplicity != RelationshipMultiplicity.Many)
301                     {
302                         // before we add a new relationship to this key entry, make sure it's not already related to something else
303                         // We have to explicitly do this here because there are no other checks to make sure a key entry in a 1-to-1 doesn't end up in two of the same relationship
304                         foreach (RelationshipEntry relationshipEntry in this.ObjectContext.ObjectStateManager.FindRelationshipsByKey(DetachedEntityKey))
305                         {
306                             // only care about relationships in the same AssociationSet and where the key is playing the same role that it plays in this EntityReference                            
307                             if (relationshipEntry.IsSameAssociationSetAndRole((AssociationSet)RelationshipSet, (AssociationEndMember)ToEndMember, DetachedEntityKey) &&
308                                 relationshipEntry.State != EntityState.Deleted)
309                             {
310                                 throw EntityUtil.EntityConflictsWithKeyEntry();
311                             }
312                         }
313                     }
314
315                     addNewRelationship = true;
316                 }
317                 else
318                 {
319                     IEntityWrapper wrappedTarget = existingEntry.WrappedEntity;
320
321                     // Verify that the target entity is in a valid state for adding a relationship
322                     if (existingEntry.State == EntityState.Deleted)
323                     {
324                         throw EntityUtil.UnableToAddRelationshipWithDeletedEntity();
325                     }
326
327                     // We know the target end of this relationship is 1..1 or 0..1 since it is a reference, so if the source end is also not Many, we have a 1-to-1
328                     RelatedEnd relatedEnd = wrappedTarget.RelationshipManager.GetRelatedEndInternal(RelationshipName, RelationshipNavigation.From);
329                     if (FromEndProperty.RelationshipMultiplicity != RelationshipMultiplicity.Many && !relatedEnd.IsEmpty())
330                     {
331                         // Make sure the target entity is not already related to something else.
332                         // devnote: The call to Add below does *not* do this check for the fixup case, so if it's not done here, no failure will occur
333                         //          and existing relationships may be deleted unexpectedly. RelatedEnd.Include should not remove existing relationships, only add new ones.
334                         throw EntityUtil.EntityConflictsWithKeyEntry();
335                     }
336
337                     // We have an existing entity with the same key, just hook up the related ends
338                     this.Add(wrappedTarget,
339                         applyConstraints: true,
340                         addRelationshipAsUnchanged: doAttach,
341                         relationshipAlreadyExists: false,
342                         allowModifyingOtherEndOfRelationship: true,
343                         forceForeignKeyChanges: true);
344
345                     // add to the list of promoted key references so we can cleanup if a failure occurs later
346                     manager.TransactionManager.PopulatedEntityReferences.Add(this);
347                 }
348             }
349
350             // For FKs, don't create a key entry and don't create a relationship
351             if (addNewRelationship && !IsForeignKey)
352             {
353                 // devnote: If we add any validation here, it needs to go here before adding the key entry,
354                 //          otherwise we have to clean up that entry if the validation fails
355
356                 if (addKeyEntry)
357                 {
358                     EntitySet targetEntitySet = DetachedEntityKey.GetEntitySet(this.ObjectContext.MetadataWorkspace);
359                     manager.AddKeyEntry(DetachedEntityKey, targetEntitySet);
360                 }
361
362                 EntityKey ownerKey = WrappedOwner.EntityKey;
363                 EntityUtil.CheckEntityKeyNull(ownerKey);
364                 RelationshipWrapper wrapper = new RelationshipWrapper((AssociationSet)RelationshipSet,
365                     RelationshipNavigation.From, ownerKey, RelationshipNavigation.To, DetachedEntityKey);
366                 manager.AddNewRelation(wrapper, doAttach ? EntityState.Unchanged : EntityState.Added);
367             }
368         }
369
370         internal override void Exclude()
371         {
372             Debug.Assert(this.ObjectContext != null, "Should not be trying to remove entities from state manager if context is null");
373
374             if (null != _wrappedCachedValue.Entity)
375             {
376                 // It is possible that _cachedValue was originally null in this graph, but was only set
377                 // while the graph was being added, if the DetachedEntityKey matched its key. In that case,
378                 // we only want to clear _cachedValue and delete the relationship entry, but not remove the entity
379                 // itself from the context.
380                 TransactionManager transManager = ObjectContext.ObjectStateManager.TransactionManager;
381                 bool doFullRemove = transManager.PopulatedEntityReferences.Contains(this);
382                 bool doRelatedEndRemove = transManager.AlignedEntityReferences.Contains(this);
383                 // For POCO, if the entity is undergoing snapshot for the first time, then in this step we actually
384                 // need to really exclude it rather than just disconnecting it.  If we don't, then it has the potential
385                 // to remain in the context at the end of the rollback process.
386                 if ((transManager.ProcessedEntities == null || !transManager.ProcessedEntities.Contains(_wrappedCachedValue)) &&
387                     (doFullRemove || doRelatedEndRemove))
388                 {
389                     // Retrieve the relationship entry before _cachedValue is set to null during Remove
390                     RelationshipEntry relationshipEntry = IsForeignKey ? null : FindRelationshipEntryInObjectStateManager(_wrappedCachedValue);
391                     Debug.Assert(IsForeignKey || relationshipEntry != null, "Should have been able to find a valid relationship since _cachedValue is non-null");
392
393                     // Remove the related ends and mark the relationship as deleted, but don't propagate the changes to the target entity itself
394                     Remove(_wrappedCachedValue,
395                             doFixup: doFullRemove,
396                             deleteEntity: false,
397                             deleteOwner: false,
398                             applyReferentialConstraints: false,
399                             preserveForeignKey: true);
400
401                     // The relationship will now either be detached (if it was previously in the Added state), or Deleted (if it was previously Unchanged)
402                     // If it's Deleted, we need to AcceptChanges to get rid of it completely                    
403                     if (relationshipEntry != null && relationshipEntry.State != EntityState.Detached)
404                     {
405                         relationshipEntry.AcceptChanges();
406                     }
407
408                     // Since this has been processed, remove it from the list
409                     if (doFullRemove)
410                     {
411                         transManager.PopulatedEntityReferences.Remove(this);
412                     }
413                     else
414                     {
415                         transManager.AlignedEntityReferences.Remove(this);
416                     }
417                 }
418                 else
419                 {
420                     ExcludeEntity(_wrappedCachedValue);
421                 }
422             }
423             else if (DetachedEntityKey != null)
424             {
425                 // there may still be relationship entries with stubs that need to be removed
426                 // this works whether we just added the key entry along with the relationship or if it was already existing
427                 ExcludeEntityKey();
428             }
429             // else there is nothing to remove for this relationship
430         }
431
432         private void ExcludeEntityKey()
433         {
434             EntityKey ownerKey = WrappedOwner.EntityKey;
435
436             RelationshipEntry relationshipEntry = this.ObjectContext.ObjectStateManager.FindRelationship(RelationshipSet,
437                 new KeyValuePair<string, EntityKey>(RelationshipNavigation.From, ownerKey),
438                 new KeyValuePair<string, EntityKey>(RelationshipNavigation.To, DetachedEntityKey));
439
440             // we may have failed in adding the graph before we actually added this relationship, so make sure we actually found one
441             if (relationshipEntry != null)
442             {
443                 relationshipEntry.Delete(/*doFixup*/ false);
444                 // If entry was Added before, it is now Detached, otherwise AcceptChanges to detach it
445                 if (relationshipEntry.State != EntityState.Detached)
446                 {
447                     relationshipEntry.AcceptChanges();
448                 }
449             }
450         }
451
452         internal override void ClearCollectionOrRef(IEntityWrapper wrappedEntity, RelationshipNavigation navigation, bool doCascadeDelete)
453         {
454             if (wrappedEntity == null)
455             {
456                 wrappedEntity = EntityWrapperFactory.NullWrapper;
457             }
458             if (null != _wrappedCachedValue.Entity)
459             {
460                 // Following condition checks if we have already visited this graph node. If its true then
461                 // we should not do fixup because that would cause circular loop
462                 if ((wrappedEntity.Entity == _wrappedCachedValue.Entity) && (navigation.Equals(this.RelationshipNavigation)))
463                 {
464                     Remove(_wrappedCachedValue, /*fixup*/false, /*deleteEntity*/false, /*deleteOwner*/false, /*applyReferentialConstraints*/false, /*preserveForeignKey*/false);
465                 }
466                 else
467                 {
468                     Remove(_wrappedCachedValue, /*fixup*/true, doCascadeDelete, /*deleteOwner*/false, /*applyReferentialConstraints*/true, /*preserveForeignKey*/false);
469                 }
470             }
471             else
472             {
473                 // this entity reference could be replacing a relationship that points to a key entry
474                 // we need to search relationships on the Owner entity to see if this is true, and if so remove the relationship entry
475                 if (WrappedOwner.Entity != null && WrappedOwner.Context != null && !UsingNoTracking)
476                 {
477                     EntityEntry ownerEntry = WrappedOwner.Context.ObjectStateManager.GetEntityEntry(WrappedOwner.Entity);
478                     ownerEntry.DeleteRelationshipsThatReferenceKeys(this.RelationshipSet, this.ToEndMember);
479                 }
480             }
481
482             // If we have an Owner, clear the DetachedEntityKey.
483             // If we do not have an owner, retain the key so that we can resolve the difference when the entity is attached to a context
484             if (this.WrappedOwner.Entity != null)
485             {
486                 // Clear the detachedEntityKey as well. In cases where we have to fix up the detachedEntityKey, we will not always be able to detect
487                 // if we have *only* a Deleted relationship for a given entity/relationship/role, so clearing this here will ensure that
488                 // even if no other relationships are added, the key value will still be correct.
489                 ((EntityReference)this).DetachedEntityKey = null;
490             }
491         }
492
493         internal override void ClearWrappedValues()
494         {
495             this._cachedValue = null;
496             this._wrappedCachedValue = NullEntityWrapper.NullWrapper;
497         }
498
499         /// <summary>
500         /// 
501         /// </summary>
502         /// <param name="entity"></param>
503         /// <param name="relationshipAlreadyExists"></param>
504         /// <returns>True if the verify succeeded, False if the Add should no-op</returns>
505         internal override bool VerifyEntityForAdd(IEntityWrapper wrappedEntity, bool relationshipAlreadyExists)
506         {
507             if (!relationshipAlreadyExists && this.ContainsEntity(wrappedEntity))
508             {
509                 return false;
510             }
511
512             this.VerifyType(wrappedEntity);
513
514             return true;
515         }
516
517         internal override bool CanSetEntityType(IEntityWrapper wrappedEntity)
518         {
519             return wrappedEntity.Entity is TEntity;
520         }
521
522         internal override void VerifyType(IEntityWrapper wrappedEntity)
523         {
524             if (!CanSetEntityType(wrappedEntity))
525             {
526                 throw EntityUtil.InvalidContainedTypeReference(wrappedEntity.Entity.GetType().FullName, typeof(TEntity).FullName);
527             }
528         }
529
530         /// <summary>
531         /// Disconnected adds are not supported for an EntityReference so we should report this as an error.
532         /// </summary>
533         /// <param name="entity">The entity to add to the related end in a disconnected state.</param>
534         internal override void DisconnectedAdd(IEntityWrapper wrappedEntity)
535         {
536             CheckOwnerNull();
537         }
538
539         /// <summary>
540         /// Disconnected removes are not supported for an EntityReference so we should report this as an error.
541         /// </summary>
542         /// <param name="entity">The entity to remove from the related end in a disconnected state.</param>
543         internal override bool DisconnectedRemove(IEntityWrapper wrappedEntity)
544         {
545             CheckOwnerNull();
546             return false;
547         }
548
549         /// <summary>
550         /// Remove from the RelatedEnd
551         /// </summary>
552         /// <param name="wrappedEntity"></param>
553         /// <param name="resetIsLoaded"></param>
554         /// <returns></returns>
555         internal override bool RemoveFromLocalCache(IEntityWrapper wrappedEntity, bool resetIsLoaded, bool preserveForeignKey)
556         {
557             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
558             Debug.Assert(null == _wrappedCachedValue.Entity || wrappedEntity.Entity == _wrappedCachedValue.Entity, "The specified object is not a part of this relationship.");
559
560             _wrappedCachedValue = EntityWrapperFactory.NullWrapper;
561             _cachedValue = null;
562
563             if (resetIsLoaded)
564             {
565                 _isLoaded = false;
566             }
567
568             // This code sets nullable FK properties on a dependent end to null when a relationship has been nulled.
569             if (ObjectContext != null && IsForeignKey && !preserveForeignKey)
570             {
571                 NullAllForeignKeys();
572             }
573             return true;
574         }
575
576         /// <summary>
577         /// Remove from the POCO collection
578         /// </summary>
579         /// <param name="wrappedEntity"></param>
580         /// <returns></returns>
581         internal override bool RemoveFromObjectCache(IEntityWrapper wrappedEntity)
582         {
583             Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
584
585             // For POCO entities - clear the CLR reference
586             if (this.TargetAccessor.HasProperty)
587             {
588                 this.WrappedOwner.RemoveNavigationPropertyValue(this, (TEntity)wrappedEntity.Entity);
589             }
590
591             return true;
592         }
593
594         // Method used to retrieve properties from principal entities.
595         // NOTE: 'properties' list is modified in this method and may already contains some properties.
596         internal override void RetrieveReferentialConstraintProperties(Dictionary<string, KeyValuePair<object, IntBox>> properties, HashSet<object> visited)
597         {
598             Debug.Assert(properties != null);
599
600             if (this._wrappedCachedValue.Entity != null)
601             {
602                 // Dictionary< propertyName, <propertyValue, counter>>
603                 Dictionary<string, KeyValuePair<object, IntBox>> retrievedProperties;
604
605                 // PERFORMANCE: ReferentialConstraints collection in typical scenario is very small (1-3 elements)
606                 foreach (ReferentialConstraint constraint in ((AssociationType)this.RelationMetadata).ReferentialConstraints)
607                 {
608                     if (constraint.ToRole == FromEndProperty)
609                     {
610                         // Detect circular references
611                         if (visited.Contains(_wrappedCachedValue))
612                         {
613                             throw EntityUtil.CircularRelationshipsWithReferentialConstraints();
614                         }
615                         visited.Add(_wrappedCachedValue);
616
617                         _wrappedCachedValue.RelationshipManager.RetrieveReferentialConstraintProperties(out retrievedProperties, visited, includeOwnValues: true);
618
619                         Debug.Assert(retrievedProperties != null);
620                         Debug.Assert(constraint.FromProperties.Count == constraint.ToProperties.Count, "Referential constraints From/To properties list have different size");
621
622                         // Following loop rewrites properties from "retrievedProperties" into "properties".
623                         // At the same time, property's name is translated from name from principal end into name from dependent end:
624                         // Example: Client<C_ID> - Order<O_ID, Client_ID>   
625                         //          Client is principal end, Order is dependent end, Client.C_ID == Order.Client_ID
626                         // Input : retrievedProperties = { "C_ID" = 123 }
627                         // Output: properties = { "Client_ID" = 123 }
628
629                         // NOTE order of properties in collections constraint.From/ToProperties is important
630                         for (int i = 0; i < constraint.FromProperties.Count; ++i)
631                         {
632                             EntityEntry.AddOrIncreaseCounter(
633                                     properties,
634                                     constraint.ToProperties[i].Name,
635                                     retrievedProperties[constraint.FromProperties[i].Name].Key);
636                         }
637                     }
638                 }
639             }
640         }
641
642         internal override bool IsEmpty()
643         {
644             return _wrappedCachedValue.Entity == null;
645         }
646
647         internal override void VerifyMultiplicityConstraintsForAdd(bool applyConstraints)
648         {
649             if (applyConstraints && !this.IsEmpty())
650             {
651                 throw EntityUtil.CannotAddMoreThanOneEntityToEntityReference(this.RelationshipNavigation.To, this.RelationshipNavigation.RelationshipName);
652             }
653         }
654
655         // Update IsLoaded flag if necessary
656         // This method is called when Clear() was called on the other end of relationship (if the other end is EntityCollection)
657         // or when Value property of the other end was set to null (if the other end is EntityReference).
658         // This method is used only when NoTracking option was used.
659         internal override void OnRelatedEndClear()
660         {
661             // If other end of relationship was loaded, it mean that this end was also cleared.
662             _isLoaded = false;
663         }
664
665         internal override bool ContainsEntity(IEntityWrapper wrappedEntity)
666         {
667             // Using operator 'as' instead of () allows calling ContainsEntity
668             // with entity of different type than TEntity.
669             return null != _wrappedCachedValue.Entity && _wrappedCachedValue.Entity == wrappedEntity.Entity;
670         }
671
672         // Identical code is in EntityCollection, but this can't be moved to the base class because it relies on the
673         // knowledge of the generic type, and the base class isn't generic
674         public ObjectQuery<TEntity> CreateSourceQuery()
675         {
676             CheckOwnerNull();
677             bool hasResults;
678             return CreateSourceQuery<TEntity>(DefaultMergeOption, out hasResults);
679         }
680
681         internal override IEnumerable CreateSourceQueryInternal()
682         {
683             return CreateSourceQuery();
684         }
685         //End identical code
686
687         /// <summary>
688         /// Take any values in the incoming RelatedEnd and sets them onto the values 
689         /// that currently exist in this RelatedEnd
690         /// </summary>
691         /// <param name="rhs"></param>
692         internal void InitializeWithValue(RelatedEnd relatedEnd)
693         {
694             Debug.Assert(this._wrappedCachedValue.Entity == null, "The EntityReference already has a value.");
695             EntityReference<TEntity> reference = relatedEnd as EntityReference<TEntity>;
696             if (reference != null && reference._wrappedCachedValue.Entity != null)
697             {
698                 _wrappedCachedValue = reference._wrappedCachedValue;
699                 _cachedValue = (TEntity)_wrappedCachedValue.Entity;
700             }
701         }
702
703         internal override bool CheckIfNavigationPropertyContainsEntity(IEntityWrapper wrapper)
704         {
705             Debug.Assert(this.RelationshipNavigation != null, "null RelationshipNavigation");
706
707             // If the navigation property doesn't exist (e.g. unidirectional prop), then it can't contain the entity.
708             if (!TargetAccessor.HasProperty)
709             {
710                 return false;
711             }
712
713             object value = this.WrappedOwner.GetNavigationPropertyValue(this);
714
715             return Object.Equals(value, wrapper.Entity);
716         }
717
718         internal override void VerifyNavigationPropertyForAdd(IEntityWrapper wrapper)
719         {
720             if (this.TargetAccessor.HasProperty)
721             {
722                 object value = WrappedOwner.GetNavigationPropertyValue(this);
723                 if (!Object.ReferenceEquals(null, value) && !Object.Equals(value, wrapper.Entity))
724                 {
725                     throw EntityUtil.CannotAddMoreThanOneEntityToEntityReference(
726                         this.RelationshipNavigation.To, this.RelationshipNavigation.RelationshipName);
727                 }
728             }
729         }
730
731         // This method is required to maintain compatibility with the v1 binary serialization format. 
732         // In particular, it recreates a entity wrapper from the serialized cached value.
733         // Note that this is only expected to work for non-POCO entities, since serialization of POCO
734         // entities will not result in serialization of the RelationshipManager or its related objects.
735         [OnDeserialized()]
736         [Browsable(false)]
737         [EditorBrowsable(EditorBrowsableState.Never)]
738         public void OnRefDeserialized(StreamingContext context)
739         {
740             _wrappedCachedValue = EntityWrapperFactory.WrapEntityUsingContext(_cachedValue, ObjectContext);
741         }
742
743         [OnSerializing()]
744         [Browsable(false)]
745         [EditorBrowsable(EditorBrowsableState.Never)]
746         public void OnSerializing(StreamingContext context)
747         {
748             if (!(WrappedOwner.Entity is IEntityWithRelationships))
749             {
750                 throw new InvalidOperationException(System.Data.Entity.Strings.RelatedEnd_CannotSerialize("EntityReference"));
751             }
752         }
753
754         #region Add
755
756         /// <summary>
757         /// AddToLocalEnd is used by both APIs a) RelatedEnd.Add b) Value property setter.
758         /// ApplyConstraints is true in case of RelatedEnd.Add because one cannot add entity to ref it its already set
759         /// however applyConstraints is false in case of Value property setter because value can be set to a new value
760         /// even if its non null.
761         /// </summary>
762         internal override void AddToLocalCache(IEntityWrapper wrappedEntity, bool applyConstraints)
763         {
764             if (wrappedEntity != _wrappedCachedValue)
765             {
766                 TransactionManager tm = ObjectContext != null ? ObjectContext.ObjectStateManager.TransactionManager : null;
767                 if (applyConstraints && null != _wrappedCachedValue.Entity)
768                 {
769                     // The idea here is that we want to throw for constraint violations in things that we are bringing in,
770                     // but not when replacing references of things already in the context.  Therefore, if the the thing that
771                     // we're replacing is in ProcessedEntities it means we're bringing it in and we should throw.
772                     if (tm == null || tm.ProcessedEntities == null || tm.ProcessedEntities.Contains(_wrappedCachedValue))
773                     {
774                         throw EntityUtil.CannotAddMoreThanOneEntityToEntityReference(this.RelationshipNavigation.To, this.RelationshipNavigation.RelationshipName);
775                     }
776                 }
777                 if (tm != null && wrappedEntity.Entity != null)
778                 {
779                     // Setting this flag will prevent the FK from being temporarily set to null while changing
780                     // it from one value to the next.
781                     tm.BeginRelatedEndAdd();
782                 }
783                 try
784                 {
785                     ClearCollectionOrRef(null, null, false);
786                     _wrappedCachedValue = wrappedEntity;
787                     _cachedValue = (TEntity)wrappedEntity.Entity;
788                 }
789                 finally
790                 {
791                     if (tm != null && tm.IsRelatedEndAdd)
792                     {
793                         tm.EndRelatedEndAdd();
794                     }
795                 }
796             }
797         }
798
799         internal override void AddToObjectCache(IEntityWrapper wrappedEntity)
800         {
801             // For POCO entities - set the CLR reference
802             if (this.TargetAccessor.HasProperty)
803             {
804                 this.WrappedOwner.SetNavigationPropertyValue(this, wrappedEntity.Entity);
805             }
806         }
807
808         #endregion
809     }
810 }
811