EntityReference.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataEntity / System / Data / Objects / DataClasses / EntityReference.cs / 4 / EntityReference.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

using System.Collections; 
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common; 
using System.Data.Common.CommandTrees;
using System.Data.Metadata.Edm; 
using System.Diagnostics; 
using System.Runtime.Serialization;
 
namespace System.Data.Objects.DataClasses
{
    /// 
    /// Models a relationship end with multiplicity 1. 
    /// 
    [DataContract] 
    [Serializable] 
    public abstract class EntityReference : RelatedEnd
    { 
        // ------
        // Fields
        // ------
 
        // The following fields are serialized.  Adding or removing a serialized field is considered
        // a breaking change.  This includes changing the field type or field name of existing 
        // serialized fields. If you need to make this kind of change, it may be possible, but it 
        // will require some custom serialization/deserialization code.
 
        // The following field is valid only for detached EntityReferences, see EntityKey property for more details.
        private EntityKey _detachedEntityKey = null;

        // ------------ 
        // Constructors
        // ------------ 
 
        /// 
        /// The default constructor is required for some serialization scenarios. It should not be used to 
        /// create new EntityReferences. Use the GetRelatedReference or GetRelatedEnd methods on the RelationshipManager
        /// class instead.
        /// 
        protected EntityReference() 
        {
        } 
 
        internal EntityReference(IEntityWithRelationships owner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer)
            : base(owner, navigation, relationshipFixer) 
        {
        }

        ///  
        /// Returns the EntityKey of the target entity associated with this EntityReference.
        /// 
        /// Is non-null in the following scenarios: 
        /// (a) Entities are tracked by a context and an Unchanged or Added client-side relationships exists for this EntityReference's owner with the
        ///     same RelationshipName and source role. This relationship could have been created explicitly by the user (e.g. by setting 
        ///     the EntityReference.Value, setting this property directly, or by calling EntityCollection.Add) or automatically through span queries.
        /// (b) If the EntityKey was non-null before detaching an entity from the context, it will still be non-null after detaching, until any operation
        ///     occurs that would set it to null, as described below.
        /// (c) Entities are detached and the EntityKey is explicitly set to non-null by the user. 
        /// (d) Entity graph was created using a NoTracking query with full span
        /// 
        /// Is null in the following scenarios: 
        /// (a) Entities are tracked by a context but there is no Unchanged or Added client-side relationship for this EntityReference's owner with the
        ///     same RelationshipName and source role. 
        /// (b) Entities are tracked by a context and a relationship exists, but the target entity has a temporary key (i.e. it is Added) or the key
        ///     is one of the special keys
        /// (c) Entities are detached and the relationship was explicitly created by the user.
        ///  
        [DataMember]
        public EntityKey EntityKey 
        { 
            // This is the only scenario where it is valid to have a null Owner, so don't check it
 
            get
            {
                if (this.ObjectContext != null && !UsingNoTracking)
                { 
                    Debug.Assert(this.Owner != null, "Unexpected null Owner on EntityReference attached to a context");
 
                    EntityKey attachedKey = null; 
                    // If this EntityReference contains an entity, look up the key on that object
                    if (CachedValue != null) 
                    {
                        // Since this EntityReference is attached to a context, the contained entity must
                        // also be attached and will therefore always have a key
                        attachedKey = this.ObjectContext.ObjectStateManager.GetEntityKey(CachedValue); 
                        if (!IsValidEntityKeyType(attachedKey))
                        { 
                            // don't return temporary or special keys from this property 
                            attachedKey = null;
                        } 
                    }
                    else
                    {
                        // There could still be an Added or Unchanged relationship with a stub entry 
                        EntityKey ownerKey = ObjectContext.FindEntityKey(this.Owner, this.ObjectContext);
                        foreach (ObjectStateEntry relationshipEntry in this.ObjectContext.ObjectStateManager.FindRelationshipsByKey(ownerKey)) 
                        { 
                            // We only care about the relationships that match the AssociationSet and source role for the owner of this EntityReference
                            if (relationshipEntry.State != EntityState.Deleted && 
                                relationshipEntry.IsSameAssociationSetAndRole((AssociationSet)RelationshipSet, ownerKey, (AssociationEndMember)this.FromEndProperty))
                            {
                                Debug.Assert(attachedKey == null, "Found more than one non-Deleted relationship for the same AssociationSet and source role");
                                attachedKey = relationshipEntry.Wrapper.GetOtherEntityKey(ownerKey); 
                                // key should never be temporary or special since it came from a key entry
                            } 
                        } 
                    }
                    Debug.Assert(attachedKey == null || IsValidEntityKeyType(attachedKey), 
                        "Unexpected temporary or special key");
                    return attachedKey;
                }
                else 
                {
                    return _detachedEntityKey; 
                } 
            }
            set 
            {
                if (value != null && value == EntityKey)
                {
                    // "no-op" -- this is not really no-op in the attached case, because at a minimum we have to do a key lookup, 
                    // worst case we have to review all relationships for the owner entity
                    // However, if we don't do this, we can get into a scenario where we are setting the key to the same thing it's already set to 
                    // and this could have side effects, especially with RI constraints and cascade delete. We don't want to delete something 
                    // and then add it back, if that deleting could have additional unexpected effects. Don't bother doing this check if value is
                    // null, because EntityKey could be null even if there are Added/Unchanged relationships, if the target entity has a temporary key. 
                    // In that case, we still need to delete that existing relationship, so it's not a no-op
                    return;
                }
 
                if (this.ObjectContext != null && !UsingNoTracking)
                { 
                    Debug.Assert(this.Owner != null, "Unexpected null Owner on EntityReference attached to a context"); 

                    // null is a valid value for the EntityKey, but temporary and special keys are not 
                    // devnote: Can't check this on detached references because this property could be set to a temp key during deserialization,
                    //          if the key hasn't finished deserializing yet.
                    if (value != null && !IsValidEntityKeyType(value))
                    { 
                        throw EntityUtil.CannotSetSpecialKeys();
                    } 
 
                    if (value == null)
                    { 
                        // delegate to Value property for all validation and firing of events
                        this.ReferenceValue = null;
                    }
                    else 
                    {
                        // Verify that the key has the right EntitySet for this RelationshipSet 
                        EntitySet targetEntitySet = value.GetEntitySet(ObjectContext.MetadataWorkspace); 
                        CheckRelationEntitySet(targetEntitySet);
                        value.ValidateEntityKey(targetEntitySet, true /*isArgumentException */, "value"); 

                        ObjectStateManager manager = this.ObjectContext.ObjectStateManager;

                        // If we already have an entry with this key, we just need to create a relationship with it 
                        bool addNewRelationship = false;
                        // If we don't already have any matching entries for this key, we'll have to create a new entry 
                        bool addKeyEntry = false; 
                        ObjectStateEntry targetEntry = manager.FindObjectStateEntry(value);
                        if (targetEntry != null) 
                        {
                            // If it's not a key entry, just use the entity to set this reference's Value
                            if (!targetEntry.IsKeyEntry)
                            { 
                                // Delegate to the Value property to clear any existing relationship
                                // and to add the new one. This will fire the appropriate events and 
                                // ensure that the related ends are connected. 

                                // It has to be a TEntity since we already verified that the EntitySet is correct above 
                                this.ReferenceValue = targetEntry.Entity;
                            }
                            else
                            { 
                                // if the existing entry is a key entry, we just need to
                                // add a new relationship between the source entity and that key 
                                addNewRelationship = true; 
                            }
                        } 
                        else
                        {
                            // no entry exists, so we'll need to add a key along with the relationship
                            addKeyEntry = true; 
                            addNewRelationship = true;
                        } 
 
                        if (addNewRelationship)
                        { 
                            EntityKey ownerKey = ValidateOwnerWithRIConstraints();

                            // Verify that the owner is in a valid state for adding a relationship
                            ValidateStateForAdd(this.Owner); 

                            if (addKeyEntry) 
                            { 
                                manager.AddKeyEntry(value, targetEntitySet);
                            } 

                            // First, clear any existing relationships
                            ClearCollectionOrRef(null, null, /*doCascadeDelete*/ false);
 
                            // Then add the new one
                            RelationshipWrapper wrapper = new RelationshipWrapper((AssociationSet)RelationshipSet, RelationshipNavigation.From, ownerKey, RelationshipNavigation.To, value); 
                            manager.AddNewRelation(wrapper, EntityState.Added); 
                        }
                    } 
                }
                else
                {
                    // Just set the field for detached object -- during Attach/Add we will make sure this value 
                    // is not in conflict if the EntityReference contains a real entity. We cannot always determine the
                    // EntityKey for any real entity in the detached state, so we don't bother to do it here. 
                    _detachedEntityKey = value; 
                }
            } 
        }

        internal EntityKey AttachedEntityKey
        { 
            get
            { 
                Debug.Assert(this.ObjectContext != null && !UsingNoTracking, "Should only need to access AttachedEntityKey property on attached EntityReferences"); 
                return this.EntityKey;
            } 
        }

        internal EntityKey DetachedEntityKey
        { 
            get
            { 
                return _detachedEntityKey; 
            }
            set 
            {
                _detachedEntityKey = value;
            }
        } 

        internal abstract object CachedValue 
        { 
            get;
        } 

        internal abstract object ReferenceValue
        {
            get; 
            set;
        } 
 

 
        internal EntityKey ValidateOwnerWithRIConstraints()
        {
            EntityKey ownerKey = ObjectStateManager.FindKeyOnEntityWithRelationships(Owner);
 
            /* Check if Referential Constraints are violated */
            if ((object)ownerKey != null && 
                !ownerKey.IsTemporary && 
                IsDependentEndOfReferentialConstraint())
            { 
                throw EntityUtil.CannotChangeReferentialConstraintProperty();
            }

            return ownerKey; 
        }
    } 
} 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

using System.Collections; 
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common; 
using System.Data.Common.CommandTrees;
using System.Data.Metadata.Edm; 
using System.Diagnostics; 
using System.Runtime.Serialization;
 
namespace System.Data.Objects.DataClasses
{
    /// 
    /// Models a relationship end with multiplicity 1. 
    /// 
    [DataContract] 
    [Serializable] 
    public abstract class EntityReference : RelatedEnd
    { 
        // ------
        // Fields
        // ------
 
        // The following fields are serialized.  Adding or removing a serialized field is considered
        // a breaking change.  This includes changing the field type or field name of existing 
        // serialized fields. If you need to make this kind of change, it may be possible, but it 
        // will require some custom serialization/deserialization code.
 
        // The following field is valid only for detached EntityReferences, see EntityKey property for more details.
        private EntityKey _detachedEntityKey = null;

        // ------------ 
        // Constructors
        // ------------ 
 
        /// 
        /// The default constructor is required for some serialization scenarios. It should not be used to 
        /// create new EntityReferences. Use the GetRelatedReference or GetRelatedEnd methods on the RelationshipManager
        /// class instead.
        /// 
        protected EntityReference() 
        {
        } 
 
        internal EntityReference(IEntityWithRelationships owner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer)
            : base(owner, navigation, relationshipFixer) 
        {
        }

        ///  
        /// Returns the EntityKey of the target entity associated with this EntityReference.
        /// 
        /// Is non-null in the following scenarios: 
        /// (a) Entities are tracked by a context and an Unchanged or Added client-side relationships exists for this EntityReference's owner with the
        ///     same RelationshipName and source role. This relationship could have been created explicitly by the user (e.g. by setting 
        ///     the EntityReference.Value, setting this property directly, or by calling EntityCollection.Add) or automatically through span queries.
        /// (b) If the EntityKey was non-null before detaching an entity from the context, it will still be non-null after detaching, until any operation
        ///     occurs that would set it to null, as described below.
        /// (c) Entities are detached and the EntityKey is explicitly set to non-null by the user. 
        /// (d) Entity graph was created using a NoTracking query with full span
        /// 
        /// Is null in the following scenarios: 
        /// (a) Entities are tracked by a context but there is no Unchanged or Added client-side relationship for this EntityReference's owner with the
        ///     same RelationshipName and source role. 
        /// (b) Entities are tracked by a context and a relationship exists, but the target entity has a temporary key (i.e. it is Added) or the key
        ///     is one of the special keys
        /// (c) Entities are detached and the relationship was explicitly created by the user.
        ///  
        [DataMember]
        public EntityKey EntityKey 
        { 
            // This is the only scenario where it is valid to have a null Owner, so don't check it
 
            get
            {
                if (this.ObjectContext != null && !UsingNoTracking)
                { 
                    Debug.Assert(this.Owner != null, "Unexpected null Owner on EntityReference attached to a context");
 
                    EntityKey attachedKey = null; 
                    // If this EntityReference contains an entity, look up the key on that object
                    if (CachedValue != null) 
                    {
                        // Since this EntityReference is attached to a context, the contained entity must
                        // also be attached and will therefore always have a key
                        attachedKey = this.ObjectContext.ObjectStateManager.GetEntityKey(CachedValue); 
                        if (!IsValidEntityKeyType(attachedKey))
                        { 
                            // don't return temporary or special keys from this property 
                            attachedKey = null;
                        } 
                    }
                    else
                    {
                        // There could still be an Added or Unchanged relationship with a stub entry 
                        EntityKey ownerKey = ObjectContext.FindEntityKey(this.Owner, this.ObjectContext);
                        foreach (ObjectStateEntry relationshipEntry in this.ObjectContext.ObjectStateManager.FindRelationshipsByKey(ownerKey)) 
                        { 
                            // We only care about the relationships that match the AssociationSet and source role for the owner of this EntityReference
                            if (relationshipEntry.State != EntityState.Deleted && 
                                relationshipEntry.IsSameAssociationSetAndRole((AssociationSet)RelationshipSet, ownerKey, (AssociationEndMember)this.FromEndProperty))
                            {
                                Debug.Assert(attachedKey == null, "Found more than one non-Deleted relationship for the same AssociationSet and source role");
                                attachedKey = relationshipEntry.Wrapper.GetOtherEntityKey(ownerKey); 
                                // key should never be temporary or special since it came from a key entry
                            } 
                        } 
                    }
                    Debug.Assert(attachedKey == null || IsValidEntityKeyType(attachedKey), 
                        "Unexpected temporary or special key");
                    return attachedKey;
                }
                else 
                {
                    return _detachedEntityKey; 
                } 
            }
            set 
            {
                if (value != null && value == EntityKey)
                {
                    // "no-op" -- this is not really no-op in the attached case, because at a minimum we have to do a key lookup, 
                    // worst case we have to review all relationships for the owner entity
                    // However, if we don't do this, we can get into a scenario where we are setting the key to the same thing it's already set to 
                    // and this could have side effects, especially with RI constraints and cascade delete. We don't want to delete something 
                    // and then add it back, if that deleting could have additional unexpected effects. Don't bother doing this check if value is
                    // null, because EntityKey could be null even if there are Added/Unchanged relationships, if the target entity has a temporary key. 
                    // In that case, we still need to delete that existing relationship, so it's not a no-op
                    return;
                }
 
                if (this.ObjectContext != null && !UsingNoTracking)
                { 
                    Debug.Assert(this.Owner != null, "Unexpected null Owner on EntityReference attached to a context"); 

                    // null is a valid value for the EntityKey, but temporary and special keys are not 
                    // devnote: Can't check this on detached references because this property could be set to a temp key during deserialization,
                    //          if the key hasn't finished deserializing yet.
                    if (value != null && !IsValidEntityKeyType(value))
                    { 
                        throw EntityUtil.CannotSetSpecialKeys();
                    } 
 
                    if (value == null)
                    { 
                        // delegate to Value property for all validation and firing of events
                        this.ReferenceValue = null;
                    }
                    else 
                    {
                        // Verify that the key has the right EntitySet for this RelationshipSet 
                        EntitySet targetEntitySet = value.GetEntitySet(ObjectContext.MetadataWorkspace); 
                        CheckRelationEntitySet(targetEntitySet);
                        value.ValidateEntityKey(targetEntitySet, true /*isArgumentException */, "value"); 

                        ObjectStateManager manager = this.ObjectContext.ObjectStateManager;

                        // If we already have an entry with this key, we just need to create a relationship with it 
                        bool addNewRelationship = false;
                        // If we don't already have any matching entries for this key, we'll have to create a new entry 
                        bool addKeyEntry = false; 
                        ObjectStateEntry targetEntry = manager.FindObjectStateEntry(value);
                        if (targetEntry != null) 
                        {
                            // If it's not a key entry, just use the entity to set this reference's Value
                            if (!targetEntry.IsKeyEntry)
                            { 
                                // Delegate to the Value property to clear any existing relationship
                                // and to add the new one. This will fire the appropriate events and 
                                // ensure that the related ends are connected. 

                                // It has to be a TEntity since we already verified that the EntitySet is correct above 
                                this.ReferenceValue = targetEntry.Entity;
                            }
                            else
                            { 
                                // if the existing entry is a key entry, we just need to
                                // add a new relationship between the source entity and that key 
                                addNewRelationship = true; 
                            }
                        } 
                        else
                        {
                            // no entry exists, so we'll need to add a key along with the relationship
                            addKeyEntry = true; 
                            addNewRelationship = true;
                        } 
 
                        if (addNewRelationship)
                        { 
                            EntityKey ownerKey = ValidateOwnerWithRIConstraints();

                            // Verify that the owner is in a valid state for adding a relationship
                            ValidateStateForAdd(this.Owner); 

                            if (addKeyEntry) 
                            { 
                                manager.AddKeyEntry(value, targetEntitySet);
                            } 

                            // First, clear any existing relationships
                            ClearCollectionOrRef(null, null, /*doCascadeDelete*/ false);
 
                            // Then add the new one
                            RelationshipWrapper wrapper = new RelationshipWrapper((AssociationSet)RelationshipSet, RelationshipNavigation.From, ownerKey, RelationshipNavigation.To, value); 
                            manager.AddNewRelation(wrapper, EntityState.Added); 
                        }
                    } 
                }
                else
                {
                    // Just set the field for detached object -- during Attach/Add we will make sure this value 
                    // is not in conflict if the EntityReference contains a real entity. We cannot always determine the
                    // EntityKey for any real entity in the detached state, so we don't bother to do it here. 
                    _detachedEntityKey = value; 
                }
            } 
        }

        internal EntityKey AttachedEntityKey
        { 
            get
            { 
                Debug.Assert(this.ObjectContext != null && !UsingNoTracking, "Should only need to access AttachedEntityKey property on attached EntityReferences"); 
                return this.EntityKey;
            } 
        }

        internal EntityKey DetachedEntityKey
        { 
            get
            { 
                return _detachedEntityKey; 
            }
            set 
            {
                _detachedEntityKey = value;
            }
        } 

        internal abstract object CachedValue 
        { 
            get;
        } 

        internal abstract object ReferenceValue
        {
            get; 
            set;
        } 
 

 
        internal EntityKey ValidateOwnerWithRIConstraints()
        {
            EntityKey ownerKey = ObjectStateManager.FindKeyOnEntityWithRelationships(Owner);
 
            /* Check if Referential Constraints are violated */
            if ((object)ownerKey != null && 
                !ownerKey.IsTemporary && 
                IsDependentEndOfReferentialConstraint())
            { 
                throw EntityUtil.CannotChangeReferentialConstraintProperty();
            }

            return ownerKey; 
        }
    } 
} 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK