EntityCollection.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 / EntityCollection.cs / 4 / EntityCollection.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.Diagnostics; 
using System.Data.Metadata.Edm; 

namespace System.Data.Objects.DataClasses 
{
    /// 
    /// Collection of entities modelling a particular EDM construct
    /// which can either be all entiteis of a particular type or 
    /// entities participating in a particular relationship.
    ///  
 
    [Serializable]
    public sealed class EntityCollection : RelatedEnd, ICollection, IListSource 
        where TEntity : class, IEntityWithRelationships
    {
        // ------
        // Fields 
        // ------
        // The following field is 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. 
        private HashSet _relatedEntities;

        [NonSerialized]
        private CollectionChangeEventHandler _onAssociationChangedforObjectView; 

 
        // ------------ 
        // Constructors
        // ------------ 

        /// 
        /// Creates an empty EntityCollection.
        ///  
        public EntityCollection()
            : base() 
        { 
        }
 
        internal EntityCollection(IEntityWithRelationships owner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer)
            : base(owner, navigation, relationshipFixer)
        {
        } 

 
        // --------- 
        // Events
        // --------- 

        /// 
        /// internal Event to notify changes in the collection.
        ///  
        // Dev notes -2
        // following statement is valid on current existing CLR: 
        // lets say Customer is an Entity, Array[Customer] is not Array[Entity]; it is not supported 
        // to do the work around we have to use a non-Generic interface/class so we can pass the EntityCollection
        // around safely (as RelatedEnd) without losing it. 
        // Dev notes -3
        // this event is only used for internal purposes, to make sure views are updated before we fire public AssociationChanged event
        internal override event CollectionChangeEventHandler AssociationChangedForObjectView {
            add { 
                _onAssociationChangedforObjectView += value;
            } 
            remove { 
                _onAssociationChangedforObjectView -= value;
            } 
        }


        // --------- 
        // Propertites
        // --------- 
        private HashSet RelatedEntities 
        {
            get { 
                if (null == _relatedEntities)
                {
                    _relatedEntities = new HashSet();
                } 
                return _relatedEntities;
            } 
        } 

        // ---------------------- 
        // ICollection Properties
        // ----------------------

        ///  
        /// Count of entities in the collection.
        ///  
        public int Count 
        {
            get 
            {
                // count should not cause allocation
                return ((null != _relatedEntities) ? _relatedEntities.Count : 0);
            } 
        }
 
 
        /// 
        /// Whether or not the collection is read-only. 
        /// 
        public bool IsReadOnly
        {
            get 
            {
                return false; 
            } 
        }
        // ---------------------- 
        // IListSource  Properties
        // ----------------------
        /// 
        ///   IListSource.ContainsListCollection implementation. Always returns true 
        /// 
        bool IListSource.ContainsListCollection 
        { 
            get
            { 
                return false; // this means that the IList we return is the one which contains our actual data, it is not a collection
            }
        }
 
        // -------
        // Methods 
        // ------- 

        internal override void OnAssociationChanged(CollectionChangeAction collectionChangeAction, object entity) 
        {
            if (!_suppressEvents)
            {
                if (_onAssociationChangedforObjectView != null) 
                {
                    _onAssociationChangedforObjectView(this, (new CollectionChangeEventArgs(collectionChangeAction, entity))); 
                } 
                if (_onAssociationChanged != null)
                { 
                    _onAssociationChanged(this, (new CollectionChangeEventArgs(collectionChangeAction, entity)));
                }
            }
        } 

        // ---------------------- 
        // IListSource  method 
        // ----------------------
        ///  
        ///   IListSource.GetList implementation
        /// 
        /// 
        ///   IList interface over the data to bind 
        /// 
        IList IListSource.GetList() 
        { 
            CheckOwnerNull();
            EntitySet singleEntitySet = null; 
            EntityType rootEntityType = null;

            // if the collection is attached, we can use metadata information; otherwise, it is unavailable
            if (null != this.RelationshipSet) 
            {
                singleEntitySet = ((AssociationSet)this.RelationshipSet).AssociationSetEnds[this.ToEndMember.Name].EntitySet; 
                EntityType associationEndType = (EntityType)((RefType)((AssociationEndMember)this.ToEndMember).TypeUsage.EdmType).ElementType; 
                EntityType entitySetType = singleEntitySet.ElementType;
 
                // the type is constrained to be either the entitySet.ElementType or the end member type, whichever is most derived
                if (associationEndType.IsAssignableFrom(entitySetType))
                {
                    // entity set exposes a subtype of the association 
                    rootEntityType = entitySetType;
                } 
                else 
                {
                    // use the end type otherwise 
                    rootEntityType = associationEndType;
                }
            }
 
            return ObjectViewFactory.CreateViewForEntityCollection(rootEntityType, this);
        } 
 
        /// 
        /// Loads the related entity or entities into the local collection using the supplied MergeOption. 
        /// Do merge if collection was already filled
        /// 
        public override void Load(MergeOption mergeOption)
        { 
            CheckOwnerNull();
 
            //Pass in null to indicate the CreateSourceQuery method should be used. 
            Load((IEnumerable)null, mergeOption);
            // do not fire the AssociationChanged event here, 
            // once it is fired in one level deeper, (at Internal void Load(IEnumerable)), you dont need to add the event at other
            // API that call (Internal void Load(IEnumerable))
        }
 
        /// 
        /// Loads related entities into the local collection. If the collection is already filled 
        /// or partially filled, merges existing entities with the given entities. The given 
        /// entities are not assumed to be the complete set of related entities.
        /// 
        /// Owner and all entities passed in must be in Unchanged or Modified state. We allow
        /// deleted elements only when the state manager is already tracking the relationship
        /// instance.
        ///  
        /// Result of query returning related entities
        /// Thrown when  is null. 
        /// Thrown when an entity in the given 
        /// collection cannot be related via the current relationship end.
        public void Attach(IEnumerable entities) 
        {
            EntityUtil.CheckArgumentNull(entities, "entities");
            CheckOwnerNull();
            Attach(entities, true); 
        }
 
        ///  
        /// Attaches an entity to the EntityCollection. If the EntityCollection is already filled
        /// or partially filled, this merges the existing entities with the given entity. The given 
        /// entity is not assumed to be the complete set of related entities.
        ///
        /// Owner and all entities passed in must be in Unchanged or Modified state.
        /// Deleted elements are allowed only when the state manager is already tracking the relationship 
        /// instance.
        ///  
        /// The entity to attach to the EntityCollection 
        /// Thrown when  is null.
        /// Thrown when the entity cannot be related via the current relationship end. 
        public void Attach(TEntity entity)
        {
            ((IRelatedEnd)this).Attach(entity);
        } 

 
        ///  
        /// Requires: collection is null or contains related entities.
        /// Loads related entities into the local collection. 
        /// 
        /// If null, retrieves entities from the server through a query;
        /// otherwise, loads the given collection
        ///  
        internal void Load(IEnumerable collection, MergeOption mergeOption)
        { 
            // Validate that the Load is possible 
            ObjectQuery sourceQuery = ValidateLoad(mergeOption, "EntityCollection");
 
            // we do not want any Add or Remove event to be fired during Merge, we will fire a Refresh event at the end if everything is successful
            _suppressEvents = true;
            try
            { 
                IEnumerable loadSource = collection ?? GetResults(sourceQuery);
                Merge(loadSource, mergeOption, true /*setIsLoaded*/); 
            } 
            finally
            { 
                _suppressEvents = false;
            }
            // fire the AssociationChange with Refresh
            OnAssociationChanged(CollectionChangeAction.Refresh, null); 
        }
 
        ///  
        ///
        ///  
        public void Add(TEntity entity)
        {
            EntityUtil.CheckArgumentNull(entity, "entity");
            if (this.Owner != null) 
            {
                ((IRelatedEnd)this).Add((TEntity)entity); 
            } 
            else
            { 
                // The EntityCollection is operating in a disconnected state
                // This is common in WCF deserialization
                DisconnectedAdd(entity);
            } 
        }
 
        ///  
        /// Add the item to the underlying collection
        ///  
        /// 
        /// 
        internal override void DisconnectedAdd(IEntityWithRelationships entity)
        { 
            // Validate that the incoming entity is also detached
            if (null != entity.RelationshipManager.Context && entity.RelationshipManager.MergeOption != MergeOption.NoTracking) 
            { 
                throw EntityUtil.UnableToAddToDisconnectedRelatedEnd();
            } 

            // Add the entity to local collection without doing any fixup
            AddEntityToLocallyCachedCollection(entity, /* applyConstraints */ false);
        } 

        ///  
        /// Remove the item from the underlying collection 
        /// 
        ///  
        /// 
        internal override bool DisconnectedRemove(IEntityWithRelationships entity)
        {
            // Validate that the incoming entity is also detached 
            if (null != entity.RelationshipManager.Context && entity.RelationshipManager.MergeOption != MergeOption.NoTracking)
            { 
                throw EntityUtil.UnableToRemoveFromDisconnectedRelatedEnd(); 
            }
 
            // Remove the entity to local collection without doing any fixup
            return RemoveEntityFromLocallyCachedCollection(entity, /* resetIsLoaded*/ false);
        }
 
        /// 
        ///   Removes an entity from the EntityCollection.  If the owner is 
        ///   attached to a context, Remove marks the relationship for deletion and if 
        ///   the relationship is composition also marks the entity for deletion.
        ///  
        /// 
        ///   Entity instance to remove from the EntityCollection
        /// 
        /// Returns true if the entity was successfully removed, false if the entity was not part of the RelatedEnd. 
        public bool Remove(TEntity entity)
        { 
            EntityUtil.CheckArgumentNull(entity, "entity"); 
            return ((IRelatedEnd)this).Remove((TEntity)entity);
        } 

        internal override void Include(bool addRelationshipAsUnchanged, bool doAttach, HashSet promotedEntityKeyRefs)
        {
            if (null != _relatedEntities && null != this.ObjectContext) 
            {
                foreach (TEntity e in _relatedEntities) 
                { 
                    IncludeEntity(e, addRelationshipAsUnchanged, doAttach, promotedEntityKeyRefs);
                } 
            }
        }

        internal override void Exclude(HashSet promotedEntityKeyRefs) 
        {
            if (null != _relatedEntities && null != this.ObjectContext) 
            { 
                foreach (TEntity e in _relatedEntities)
                { 
                    ExcludeEntity(e, promotedEntityKeyRefs);
                }
            }
        } 

        internal override void ClearCollectionOrRef(IEntityWithRelationships entity, RelationshipNavigation navigation, bool doCascadeDelete) 
        { 
            if (null != _relatedEntities)
            { 
                //copy into list because changing collection member is not allowed during enumeration.
                // If possible avoid copying into list.
                List tempCopy = new List(_relatedEntities);
                foreach (TEntity e in tempCopy) 
                {
                    // Following condition checks if we have already visited this graph node. If its true then 
                    // we should not do fixup because that would cause circular loop 
                    if ((entity == e) && (navigation.Equals(RelationshipNavigation)))
                    { 
                        Remove(e, /*fixup*/false, /*deleteEntity*/false, /*deleteOwner*/false, /*applyReferentialConstraints*/false);
                    }
                    else
                    { 
                        Remove(e, /*fixup*/true, doCascadeDelete, /*deleteOwner*/false, /*applyReferentialConstraints*/false);
                    } 
                } 
                Debug.Assert(_relatedEntities.Count == 0, "After removing all related entities local collection count should be zero");
            } 
        }

        /// 
        /// 
        /// 
        ///  
        ///  
        /// True if the verify succeeded, False if the Add should no-op
        internal override bool VerifyEntityForAdd(IEntityWithRelationships entity, bool relationshipAlreadyExists) 
        {
            if (!relationshipAlreadyExists && this.ContainsEntity(entity))
            {
                return false; 
            }
 
            if (!(entity is TEntity)) 
            {
                throw EntityUtil.InvalidContainedTypeCollection(entity.GetType().FullName, typeof(TEntity).FullName); 
            }
            return true;
        }
 
        //applyConstraints flag is only used in EntityReference
        internal override void AddEntityToLocallyCachedCollection(IEntityWithRelationships entity, bool applyConstraints) 
        { 
            RelatedEntities.Add((TEntity)entity);
        } 

        internal override bool RemoveEntityFromLocallyCachedCollection(IEntityWithRelationships entity, bool resetIsLoaded)
        {
            if (_relatedEntities != null && _relatedEntities.Remove((TEntity)entity)) 
            {
                if (resetIsLoaded) 
                    _isLoaded = false; 
                return true;
            } 
            return false;
        }

        internal override void RetrieveReferentialConstraintProperties(Dictionary> properties, HashSet visited) 
        {
            // Since there are no RI Constraints which has a collection as a To/Child role, 
            // this method is no-op. 
        }
 
        internal override bool IsEmpty()
        {
            return _relatedEntities == null || (_relatedEntities.Count == 0);
        } 

        // Update IsLoaded flag if necessary 
        // This method is called when Clear() was called on the other end of relationship (if the other end is EntityCollection) 
        // or when Value property of the other end was set to null (if the other end is EntityReference).
        // This method is used only when NoTracking option was used. 
        internal override void OnRelatedEndClear()
        {
            // If other end of relationship was cleared, it means that this collection is also no longer loaded
            _isLoaded = false; 
        }
 
        internal override bool ContainsEntity(IEntityWithRelationships entity) 
        {
            // Using operator 'as' instead of () allows calling ContainsEntity 
            // with entity of different type than TEntity.
            return Contains(entity as TEntity);
        }
 
        // -------------------
        // ICollection Methods 
        // ------------------- 

        ///  
        ///   Get an enumerator for the collection.
        /// 
        public new IEnumerator GetEnumerator()
        { 
            return RelatedEntities.GetEnumerator();
        } 
 
        IEnumerator System.Collections.IEnumerable.GetEnumerator()
        { 
            return RelatedEntities.GetEnumerator();
        }

        internal override IEnumerator GetInternalEnumerator() 
        {
            return RelatedEntities.GetEnumerator(); 
        } 

        ///  
        /// Removes all entities from the locally cached collection.  Also removes
        /// relationships related to this entities from the ObjectStateManager.
        /// 
        public void Clear() 
        {
            if (Owner != null) 
            { 
                bool shouldFireEvent = (Count > 0);
                if (null != _relatedEntities) 
                {

                    List affectedEntities = new List(_relatedEntities);
 
                    try
                    { 
                        _suppressEvents = true; 

                        foreach (TEntity entity in affectedEntities) 
                        {
                            // Remove Entity
                            Remove(entity);
 
                            if (UsingNoTracking)
                            { 
                                // The other end of relationship can be the EntityReference or EntityCollection 
                                // If the other end is EntityReference, its IsLoaded property should be set to FALSE
                                RelatedEnd relatedEnd = GetOtherEndOfRelationship(entity); 
                                relatedEnd.OnRelatedEndClear();
                            }
                        }
                        Debug.Assert(_relatedEntities.Count == 0); 
                    }
                    finally 
                    { 
                        _suppressEvents = false;
                    } 

                    if (UsingNoTracking)
                    {
                        _isLoaded = false; 
                    }
                } 
 
                if (shouldFireEvent)
                { 
                    OnAssociationChanged(CollectionChangeAction.Refresh, null);
                }
            }
            else 
            {
                // Disconnected Clear should be dispatched to the internal collection 
                if (_relatedEntities != null) 
                {
                    _relatedEntities.Clear(); 
                }
            }
        }
 
        /// 
        /// Determine if the collection contains a specific object by reference. 
        ///  
        /// true if the collection contains the object by reference;
        /// otherwise, false 
        public bool Contains(TEntity entity)
        {
            return _relatedEntities == null ? false : _relatedEntities.Contains(entity);
        } 

        ///  
        /// Copies the contents of the collection to an array, 
        /// starting at a particular array index.
        ///  
        public void CopyTo(TEntity[] array, int arrayIndex)
        {
            RelatedEntities.CopyTo(array, arrayIndex);
        } 

        internal override void BulkDeleteAll(List list) 
        { 
            if (list.Count > 0)
            { 
                _suppressEvents = true;
                try
                {
                    foreach (IEntityWithRelationships entity in list) 
                    {
                        // Remove Entity 
                         Remove(entity as TEntity); 
                    }
                } 
                finally
                {
                    _suppressEvents = false;
                } 
                OnAssociationChanged(CollectionChangeAction.Refresh, null);
            } 
        } 

        // Identical code is in EntityReference, but this can't be moved to the base class because it relies on the 
        // knowledge of the generic type, and the base class isn't generic
        public ObjectQuery CreateSourceQuery()
        {
            CheckOwnerNull(); 
            return CreateSourceQuery(DefaultMergeOption);
        } 
 
        internal override IEnumerable CreateSourceQueryInternal()
        { 
            return CreateSourceQuery();
        }
        //End identical code
    } 
}

// 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.Diagnostics; 
using System.Data.Metadata.Edm; 

namespace System.Data.Objects.DataClasses 
{
    /// 
    /// Collection of entities modelling a particular EDM construct
    /// which can either be all entiteis of a particular type or 
    /// entities participating in a particular relationship.
    ///  
 
    [Serializable]
    public sealed class EntityCollection : RelatedEnd, ICollection, IListSource 
        where TEntity : class, IEntityWithRelationships
    {
        // ------
        // Fields 
        // ------
        // The following field is 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. 
        private HashSet _relatedEntities;

        [NonSerialized]
        private CollectionChangeEventHandler _onAssociationChangedforObjectView; 

 
        // ------------ 
        // Constructors
        // ------------ 

        /// 
        /// Creates an empty EntityCollection.
        ///  
        public EntityCollection()
            : base() 
        { 
        }
 
        internal EntityCollection(IEntityWithRelationships owner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer)
            : base(owner, navigation, relationshipFixer)
        {
        } 

 
        // --------- 
        // Events
        // --------- 

        /// 
        /// internal Event to notify changes in the collection.
        ///  
        // Dev notes -2
        // following statement is valid on current existing CLR: 
        // lets say Customer is an Entity, Array[Customer] is not Array[Entity]; it is not supported 
        // to do the work around we have to use a non-Generic interface/class so we can pass the EntityCollection
        // around safely (as RelatedEnd) without losing it. 
        // Dev notes -3
        // this event is only used for internal purposes, to make sure views are updated before we fire public AssociationChanged event
        internal override event CollectionChangeEventHandler AssociationChangedForObjectView {
            add { 
                _onAssociationChangedforObjectView += value;
            } 
            remove { 
                _onAssociationChangedforObjectView -= value;
            } 
        }


        // --------- 
        // Propertites
        // --------- 
        private HashSet RelatedEntities 
        {
            get { 
                if (null == _relatedEntities)
                {
                    _relatedEntities = new HashSet();
                } 
                return _relatedEntities;
            } 
        } 

        // ---------------------- 
        // ICollection Properties
        // ----------------------

        ///  
        /// Count of entities in the collection.
        ///  
        public int Count 
        {
            get 
            {
                // count should not cause allocation
                return ((null != _relatedEntities) ? _relatedEntities.Count : 0);
            } 
        }
 
 
        /// 
        /// Whether or not the collection is read-only. 
        /// 
        public bool IsReadOnly
        {
            get 
            {
                return false; 
            } 
        }
        // ---------------------- 
        // IListSource  Properties
        // ----------------------
        /// 
        ///   IListSource.ContainsListCollection implementation. Always returns true 
        /// 
        bool IListSource.ContainsListCollection 
        { 
            get
            { 
                return false; // this means that the IList we return is the one which contains our actual data, it is not a collection
            }
        }
 
        // -------
        // Methods 
        // ------- 

        internal override void OnAssociationChanged(CollectionChangeAction collectionChangeAction, object entity) 
        {
            if (!_suppressEvents)
            {
                if (_onAssociationChangedforObjectView != null) 
                {
                    _onAssociationChangedforObjectView(this, (new CollectionChangeEventArgs(collectionChangeAction, entity))); 
                } 
                if (_onAssociationChanged != null)
                { 
                    _onAssociationChanged(this, (new CollectionChangeEventArgs(collectionChangeAction, entity)));
                }
            }
        } 

        // ---------------------- 
        // IListSource  method 
        // ----------------------
        ///  
        ///   IListSource.GetList implementation
        /// 
        /// 
        ///   IList interface over the data to bind 
        /// 
        IList IListSource.GetList() 
        { 
            CheckOwnerNull();
            EntitySet singleEntitySet = null; 
            EntityType rootEntityType = null;

            // if the collection is attached, we can use metadata information; otherwise, it is unavailable
            if (null != this.RelationshipSet) 
            {
                singleEntitySet = ((AssociationSet)this.RelationshipSet).AssociationSetEnds[this.ToEndMember.Name].EntitySet; 
                EntityType associationEndType = (EntityType)((RefType)((AssociationEndMember)this.ToEndMember).TypeUsage.EdmType).ElementType; 
                EntityType entitySetType = singleEntitySet.ElementType;
 
                // the type is constrained to be either the entitySet.ElementType or the end member type, whichever is most derived
                if (associationEndType.IsAssignableFrom(entitySetType))
                {
                    // entity set exposes a subtype of the association 
                    rootEntityType = entitySetType;
                } 
                else 
                {
                    // use the end type otherwise 
                    rootEntityType = associationEndType;
                }
            }
 
            return ObjectViewFactory.CreateViewForEntityCollection(rootEntityType, this);
        } 
 
        /// 
        /// Loads the related entity or entities into the local collection using the supplied MergeOption. 
        /// Do merge if collection was already filled
        /// 
        public override void Load(MergeOption mergeOption)
        { 
            CheckOwnerNull();
 
            //Pass in null to indicate the CreateSourceQuery method should be used. 
            Load((IEnumerable)null, mergeOption);
            // do not fire the AssociationChanged event here, 
            // once it is fired in one level deeper, (at Internal void Load(IEnumerable)), you dont need to add the event at other
            // API that call (Internal void Load(IEnumerable))
        }
 
        /// 
        /// Loads related entities into the local collection. If the collection is already filled 
        /// or partially filled, merges existing entities with the given entities. The given 
        /// entities are not assumed to be the complete set of related entities.
        /// 
        /// Owner and all entities passed in must be in Unchanged or Modified state. We allow
        /// deleted elements only when the state manager is already tracking the relationship
        /// instance.
        ///  
        /// Result of query returning related entities
        /// Thrown when  is null. 
        /// Thrown when an entity in the given 
        /// collection cannot be related via the current relationship end.
        public void Attach(IEnumerable entities) 
        {
            EntityUtil.CheckArgumentNull(entities, "entities");
            CheckOwnerNull();
            Attach(entities, true); 
        }
 
        ///  
        /// Attaches an entity to the EntityCollection. If the EntityCollection is already filled
        /// or partially filled, this merges the existing entities with the given entity. The given 
        /// entity is not assumed to be the complete set of related entities.
        ///
        /// Owner and all entities passed in must be in Unchanged or Modified state.
        /// Deleted elements are allowed only when the state manager is already tracking the relationship 
        /// instance.
        ///  
        /// The entity to attach to the EntityCollection 
        /// Thrown when  is null.
        /// Thrown when the entity cannot be related via the current relationship end. 
        public void Attach(TEntity entity)
        {
            ((IRelatedEnd)this).Attach(entity);
        } 

 
        ///  
        /// Requires: collection is null or contains related entities.
        /// Loads related entities into the local collection. 
        /// 
        /// If null, retrieves entities from the server through a query;
        /// otherwise, loads the given collection
        ///  
        internal void Load(IEnumerable collection, MergeOption mergeOption)
        { 
            // Validate that the Load is possible 
            ObjectQuery sourceQuery = ValidateLoad(mergeOption, "EntityCollection");
 
            // we do not want any Add or Remove event to be fired during Merge, we will fire a Refresh event at the end if everything is successful
            _suppressEvents = true;
            try
            { 
                IEnumerable loadSource = collection ?? GetResults(sourceQuery);
                Merge(loadSource, mergeOption, true /*setIsLoaded*/); 
            } 
            finally
            { 
                _suppressEvents = false;
            }
            // fire the AssociationChange with Refresh
            OnAssociationChanged(CollectionChangeAction.Refresh, null); 
        }
 
        ///  
        ///
        ///  
        public void Add(TEntity entity)
        {
            EntityUtil.CheckArgumentNull(entity, "entity");
            if (this.Owner != null) 
            {
                ((IRelatedEnd)this).Add((TEntity)entity); 
            } 
            else
            { 
                // The EntityCollection is operating in a disconnected state
                // This is common in WCF deserialization
                DisconnectedAdd(entity);
            } 
        }
 
        ///  
        /// Add the item to the underlying collection
        ///  
        /// 
        /// 
        internal override void DisconnectedAdd(IEntityWithRelationships entity)
        { 
            // Validate that the incoming entity is also detached
            if (null != entity.RelationshipManager.Context && entity.RelationshipManager.MergeOption != MergeOption.NoTracking) 
            { 
                throw EntityUtil.UnableToAddToDisconnectedRelatedEnd();
            } 

            // Add the entity to local collection without doing any fixup
            AddEntityToLocallyCachedCollection(entity, /* applyConstraints */ false);
        } 

        ///  
        /// Remove the item from the underlying collection 
        /// 
        ///  
        /// 
        internal override bool DisconnectedRemove(IEntityWithRelationships entity)
        {
            // Validate that the incoming entity is also detached 
            if (null != entity.RelationshipManager.Context && entity.RelationshipManager.MergeOption != MergeOption.NoTracking)
            { 
                throw EntityUtil.UnableToRemoveFromDisconnectedRelatedEnd(); 
            }
 
            // Remove the entity to local collection without doing any fixup
            return RemoveEntityFromLocallyCachedCollection(entity, /* resetIsLoaded*/ false);
        }
 
        /// 
        ///   Removes an entity from the EntityCollection.  If the owner is 
        ///   attached to a context, Remove marks the relationship for deletion and if 
        ///   the relationship is composition also marks the entity for deletion.
        ///  
        /// 
        ///   Entity instance to remove from the EntityCollection
        /// 
        /// Returns true if the entity was successfully removed, false if the entity was not part of the RelatedEnd. 
        public bool Remove(TEntity entity)
        { 
            EntityUtil.CheckArgumentNull(entity, "entity"); 
            return ((IRelatedEnd)this).Remove((TEntity)entity);
        } 

        internal override void Include(bool addRelationshipAsUnchanged, bool doAttach, HashSet promotedEntityKeyRefs)
        {
            if (null != _relatedEntities && null != this.ObjectContext) 
            {
                foreach (TEntity e in _relatedEntities) 
                { 
                    IncludeEntity(e, addRelationshipAsUnchanged, doAttach, promotedEntityKeyRefs);
                } 
            }
        }

        internal override void Exclude(HashSet promotedEntityKeyRefs) 
        {
            if (null != _relatedEntities && null != this.ObjectContext) 
            { 
                foreach (TEntity e in _relatedEntities)
                { 
                    ExcludeEntity(e, promotedEntityKeyRefs);
                }
            }
        } 

        internal override void ClearCollectionOrRef(IEntityWithRelationships entity, RelationshipNavigation navigation, bool doCascadeDelete) 
        { 
            if (null != _relatedEntities)
            { 
                //copy into list because changing collection member is not allowed during enumeration.
                // If possible avoid copying into list.
                List tempCopy = new List(_relatedEntities);
                foreach (TEntity e in tempCopy) 
                {
                    // Following condition checks if we have already visited this graph node. If its true then 
                    // we should not do fixup because that would cause circular loop 
                    if ((entity == e) && (navigation.Equals(RelationshipNavigation)))
                    { 
                        Remove(e, /*fixup*/false, /*deleteEntity*/false, /*deleteOwner*/false, /*applyReferentialConstraints*/false);
                    }
                    else
                    { 
                        Remove(e, /*fixup*/true, doCascadeDelete, /*deleteOwner*/false, /*applyReferentialConstraints*/false);
                    } 
                } 
                Debug.Assert(_relatedEntities.Count == 0, "After removing all related entities local collection count should be zero");
            } 
        }

        /// 
        /// 
        /// 
        ///  
        ///  
        /// True if the verify succeeded, False if the Add should no-op
        internal override bool VerifyEntityForAdd(IEntityWithRelationships entity, bool relationshipAlreadyExists) 
        {
            if (!relationshipAlreadyExists && this.ContainsEntity(entity))
            {
                return false; 
            }
 
            if (!(entity is TEntity)) 
            {
                throw EntityUtil.InvalidContainedTypeCollection(entity.GetType().FullName, typeof(TEntity).FullName); 
            }
            return true;
        }
 
        //applyConstraints flag is only used in EntityReference
        internal override void AddEntityToLocallyCachedCollection(IEntityWithRelationships entity, bool applyConstraints) 
        { 
            RelatedEntities.Add((TEntity)entity);
        } 

        internal override bool RemoveEntityFromLocallyCachedCollection(IEntityWithRelationships entity, bool resetIsLoaded)
        {
            if (_relatedEntities != null && _relatedEntities.Remove((TEntity)entity)) 
            {
                if (resetIsLoaded) 
                    _isLoaded = false; 
                return true;
            } 
            return false;
        }

        internal override void RetrieveReferentialConstraintProperties(Dictionary> properties, HashSet visited) 
        {
            // Since there are no RI Constraints which has a collection as a To/Child role, 
            // this method is no-op. 
        }
 
        internal override bool IsEmpty()
        {
            return _relatedEntities == null || (_relatedEntities.Count == 0);
        } 

        // Update IsLoaded flag if necessary 
        // This method is called when Clear() was called on the other end of relationship (if the other end is EntityCollection) 
        // or when Value property of the other end was set to null (if the other end is EntityReference).
        // This method is used only when NoTracking option was used. 
        internal override void OnRelatedEndClear()
        {
            // If other end of relationship was cleared, it means that this collection is also no longer loaded
            _isLoaded = false; 
        }
 
        internal override bool ContainsEntity(IEntityWithRelationships entity) 
        {
            // Using operator 'as' instead of () allows calling ContainsEntity 
            // with entity of different type than TEntity.
            return Contains(entity as TEntity);
        }
 
        // -------------------
        // ICollection Methods 
        // ------------------- 

        ///  
        ///   Get an enumerator for the collection.
        /// 
        public new IEnumerator GetEnumerator()
        { 
            return RelatedEntities.GetEnumerator();
        } 
 
        IEnumerator System.Collections.IEnumerable.GetEnumerator()
        { 
            return RelatedEntities.GetEnumerator();
        }

        internal override IEnumerator GetInternalEnumerator() 
        {
            return RelatedEntities.GetEnumerator(); 
        } 

        ///  
        /// Removes all entities from the locally cached collection.  Also removes
        /// relationships related to this entities from the ObjectStateManager.
        /// 
        public void Clear() 
        {
            if (Owner != null) 
            { 
                bool shouldFireEvent = (Count > 0);
                if (null != _relatedEntities) 
                {

                    List affectedEntities = new List(_relatedEntities);
 
                    try
                    { 
                        _suppressEvents = true; 

                        foreach (TEntity entity in affectedEntities) 
                        {
                            // Remove Entity
                            Remove(entity);
 
                            if (UsingNoTracking)
                            { 
                                // The other end of relationship can be the EntityReference or EntityCollection 
                                // If the other end is EntityReference, its IsLoaded property should be set to FALSE
                                RelatedEnd relatedEnd = GetOtherEndOfRelationship(entity); 
                                relatedEnd.OnRelatedEndClear();
                            }
                        }
                        Debug.Assert(_relatedEntities.Count == 0); 
                    }
                    finally 
                    { 
                        _suppressEvents = false;
                    } 

                    if (UsingNoTracking)
                    {
                        _isLoaded = false; 
                    }
                } 
 
                if (shouldFireEvent)
                { 
                    OnAssociationChanged(CollectionChangeAction.Refresh, null);
                }
            }
            else 
            {
                // Disconnected Clear should be dispatched to the internal collection 
                if (_relatedEntities != null) 
                {
                    _relatedEntities.Clear(); 
                }
            }
        }
 
        /// 
        /// Determine if the collection contains a specific object by reference. 
        ///  
        /// true if the collection contains the object by reference;
        /// otherwise, false 
        public bool Contains(TEntity entity)
        {
            return _relatedEntities == null ? false : _relatedEntities.Contains(entity);
        } 

        ///  
        /// Copies the contents of the collection to an array, 
        /// starting at a particular array index.
        ///  
        public void CopyTo(TEntity[] array, int arrayIndex)
        {
            RelatedEntities.CopyTo(array, arrayIndex);
        } 

        internal override void BulkDeleteAll(List list) 
        { 
            if (list.Count > 0)
            { 
                _suppressEvents = true;
                try
                {
                    foreach (IEntityWithRelationships entity in list) 
                    {
                        // Remove Entity 
                         Remove(entity as TEntity); 
                    }
                } 
                finally
                {
                    _suppressEvents = false;
                } 
                OnAssociationChanged(CollectionChangeAction.Refresh, null);
            } 
        } 

        // Identical code is in EntityReference, but this can't be moved to the base class because it relies on the 
        // knowledge of the generic type, and the base class isn't generic
        public ObjectQuery CreateSourceQuery()
        {
            CheckOwnerNull(); 
            return CreateSourceQuery(DefaultMergeOption);
        } 
 
        internal override IEnumerable CreateSourceQueryInternal()
        { 
            return CreateSourceQuery();
        }
        //End identical code
    } 
}

// 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