shaper.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 / Common / internal / materialization / shaper.cs / 3 / shaper.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
// [....]
//----------------------------------------------------------------------------- 
 
using System.Collections.Generic;
using System.Data.Common.Utils; 
using System.Linq;
using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Data.Objects.DataClasses; 
using System.Diagnostics;
using System.Reflection; 
 
namespace System.Data.Common.Internal.Materialization
{ 
    /// 
    /// Shapes store reader values into EntityClient/ObjectQuery results. Also maintains
    /// state used by materializer delegates.
    ///  
    internal abstract class Shaper
    { 
        #region constructor 

        internal Shaper(DbDataReader reader, ObjectContext context, MetadataWorkspace workspace, MergeOption mergeOption, int stateCount) 
        {
            Debug.Assert(context == null || workspace == context.MetadataWorkspace, "workspace must match context's workspace");

            this.Reader = reader; 
            this.MergeOption = mergeOption;
            this.State = new object[stateCount]; 
            this.Context = context; 
            this.Workspace = workspace;
        } 

        #endregion

        #region runtime callable/accessible code 

        // Code in this section is called from the delegates produced by the Translator.  It 
        // may not show up if you search using Find All References...use Find in Files instead. 
        //
        // Many items on this class are public, simply to make the job of producing the 
        // expressions that use them simpler.  If you have a hankering to make them private,
        // you will need to modify the code in the Translator that does the GetMethod/GetField
        // to use BindingFlags.NonPublic | BindingFlags.Instance as well.
        // 
        // Debug.Asserts that fire from the code in this region will probably create a
        // SecurityException in the Coordinator's Read method since those are restricted when 
        // running the Shaper. 

        ///  
        /// The store data reader we're pulling data from
        /// 
        public readonly DbDataReader Reader;
 
        /// 
        /// The state slots we use in the coordinator expression. 
        ///  
        public readonly object[] State;
 
        /// 
        /// The context the shaper is performing for.
        /// 
        public readonly ObjectContext Context; 

        ///  
        /// The workspace we are performing for; yes we could get it from the context, but 
        /// it's much easier to just have it handy.
        ///  
        public readonly MetadataWorkspace Workspace;

        /// 
        /// The merge option this shaper is performing under/for. 
        /// 
        public readonly MergeOption MergeOption; 
 
        /// 
        /// Utility method used to evaluate a multi-discriminator column map. Takes 
        /// discriminator values and determines the appropriate entity type, then looks up
        /// the appropriate handler and invokes it.
        /// 
        public TElement Discriminate(object[] discriminatorValues, Func discriminate, KeyValuePair>[] elementDelegates) 
        {
            EntityType entityType = discriminate(discriminatorValues); 
            Func elementDelegate = null; 
            foreach (KeyValuePair> typeDelegatePair in elementDelegates)
            { 
                if (typeDelegatePair.Key == entityType)
                {
                    elementDelegate = typeDelegatePair.Value;
                } 
            }
            return elementDelegate(this); 
        } 

        ///  
        /// REQUIRES:: entity is not null and MergeOption is OverwriteChanges or PreserveChanges
        /// Handles state management for an entity returned by a query. Where an existing entry
        /// exists, updates that entry and returns the existing entity. Otherwise, the entity
        /// passed in is returned. 
        /// 
        public TEntity HandleEntity(TEntity entity, EntityKey entityKey, EntitySet entitySet) 
        { 
            Debug.Assert(MergeOption.NoTracking != this.MergeOption, "no need to HandleEntity if there's no tracking");
            Debug.Assert(MergeOption.AppendOnly != this.MergeOption, "use HandleEntityAppendOnly instead..."); 
            Debug.Assert(null != entity, "if HandleEntity is called, there must be an entity");

            TEntity result = entity;
 
            // no entity set, so no tracking is required for this entity
            if (null != (object)entityKey) 
            { 
                Debug.Assert(null != entitySet, "if there is an entity key, there must also be an entity set");
 
                // check for an existing entity with the same key
                ObjectStateEntry existingEntry = this.Context.ObjectStateManager.FindObjectStateEntry(entityKey);
                if (null != existingEntry && !existingEntry.IsKeyEntry)
                { 
                    Debug.Assert(existingEntry.EntityKey.Equals(entityKey), "Found ObjectStateEntry with wrong EntityKey");
                    UpdateEntry(entity, existingEntry); 
                    result = (TEntity)existingEntry.Entity; 
                }
                else 
                {
                    // if the entity isn't tracked yet, attach it
                    if (null == existingEntry)
                    { 
                        Context.ObjectStateManager.AddEntry(entity, entityKey, entitySet, "HandleEntity", false);
                    } 
                    else 
                    {
                        Context.ObjectStateManager.PromoteKeyEntry(existingEntry, entity, (IExtendedDataRecord)null, false, /*setIsLoaded*/ true, /*keyEntryInitialized*/ false, "HandleEntity"); 
                    }
                }
            }
            return result; 
        }
 
        ///  
        /// REQUIRES:: entity exists; MergeOption is AppendOnly
        /// Handles state management for an entity with the given key. When the entity already exists 
        /// in the state manager, it is returned directly. Otherwise, the entityDelegate is invoked and
        /// the resulting entity is returned.
        /// 
        public TEntity HandleEntityAppendOnly(Func constructEntityDelegate, EntityKey entityKey, EntitySet entitySet) 
        {
            Debug.Assert(this.MergeOption == MergeOption.AppendOnly, "only use HandleEntityAppendOnly when MergeOption is AppendOnly"); 
            Debug.Assert(null != constructEntityDelegate, "must provide delegate to construct the entity"); 

            TEntity result; 

            if (null == (object)entityKey)
            {
                // no entity set, so no tracking is required for this entity, just 
                // call the delegate to "materialize" it.
                result = constructEntityDelegate(this); 
            } 
            else
            { 
                Debug.Assert(null != entitySet, "if there is an entity key, there must also be an entity set");

                // check for an existing entity with the same key
                ObjectStateEntry existingEntry = this.Context.ObjectStateManager.FindObjectStateEntry(entityKey); 
                if (null != existingEntry && !existingEntry.IsKeyEntry)
                { 
                    Debug.Assert(existingEntry.EntityKey.Equals(entityKey), "Found ObjectStateEntry with wrong EntityKey"); 
                    if (typeof(TEntity) != existingEntry.Entity.GetType())
                    { 
                        throw EntityUtil.RecyclingEntity(existingEntry.EntityKey, typeof(TEntity), existingEntry.Entity.GetType());
                    }

                    if (EntityState.Added == existingEntry.State) 
                    {
                        throw EntityUtil.AddedEntityAlreadyExists(existingEntry.EntityKey); 
                    } 
                    result = (TEntity)existingEntry.Entity;
                } 
                else
                {
                    // We don't already have the entity, so construct it and if it isn't
                    // tracked yet, attach it 
                    result = constructEntityDelegate(this);
                    if (null == existingEntry) 
                    { 
                        Context.ObjectStateManager.AddEntry(result, entityKey, entitySet, "HandleEntity", false);
                    } 
                    else
                    {
                        Context.ObjectStateManager.PromoteKeyEntry(existingEntry, result, (IExtendedDataRecord)null, false, /*setIsLoaded*/ true, /*keyEntryInitialized*/ false, "HandleEntity");
                    } 
                }
            } 
            return result; 
        }
 
        /// 
        /// Call to ensure a collection of full-spanned elements are added
        /// into the state manager properly.  We registers an action to be called
        /// when the collection is closed that pulls the collection of full spanned 
        /// objects into the state manager.
        ///  
        public T_SourceEntity HandleFullSpanCollection(T_SourceEntity entity, Coordinator coordinator, AssociationEndMember targetMember) 
        {
            IEntityWithRelationships sourceEntity = entity as IEntityWithRelationships; 
            if (null != sourceEntity)
            {
                coordinator.RegisterCloseHandler((state, spannedEntities) => FullSpanAction(sourceEntity, spannedEntities, targetMember));
            } 
            return entity;
        } 
 
        /// 
        /// Call to ensure a single full-spanned element is added into 
        /// the state manager properly.
        /// 
        public T_SourceEntity HandleFullSpanElement(T_SourceEntity entity, T_TargetEntity spannedEntity, AssociationEndMember targetMember)
        { 
            IEntityWithRelationships sourceEntity = entity as IEntityWithRelationships;
            if (null != sourceEntity) 
            { 
                List spannedEntities = null;
                if (spannedEntity != null) 
                {
                    // There was a single entity in the column
                    // Create a list so we can perform the same logic as a collection of entities
                    spannedEntities = new List(1); 
                    spannedEntities.Add(spannedEntity);
                } 
                else 
                {
                    EntityKey sourceKey = ObjectStateManager.FindKeyOnEntityWithRelationships(sourceEntity); 
                    CheckClearedEntryOnSpan(spannedEntity, sourceEntity, sourceKey, targetMember);
                }
                FullSpanAction(sourceEntity, spannedEntities, targetMember);
            } 
            return entity;
        } 
 
        /// 
        /// Call to ensure a target entities key is added into the state manager 
        /// properly
        /// 
        public T_SourceEntity HandleRelationshipSpan(T_SourceEntity entity, EntityKey targetKey, AssociationEndMember targetMember)
        { 
            IEntityWithRelationships sourceEntity = entity as IEntityWithRelationships;
            if (null == sourceEntity) 
            { 
                return entity;
            } 
            Debug.Assert(targetMember != null);
            Debug.Assert(targetMember.RelationshipMultiplicity == RelationshipMultiplicity.One || targetMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne);

            EntityUtil.CheckKeyForRelationship(sourceEntity, MergeOption); 
            EntityKey sourceKey = ObjectStateManager.FindKeyOnEntityWithRelationships(sourceEntity);
            AssociationEndMember sourceMember = MetadataHelper.GetOtherAssociationEnd(targetMember); 
            CheckClearedEntryOnSpan(targetKey, sourceEntity, sourceKey, targetMember); 

            if (null != (object)targetKey) 
            {
                EntitySet targetEntitySet;

                EntityContainer entityContainer = this.Context.MetadataWorkspace.GetEntityContainer( 
                    targetKey.EntityContainerName, DataSpace.CSpace);
 
                // find the correct AssociationSet 
                AssociationSet associationSet = MetadataHelper.GetAssociationsForEntitySetAndAssociationType(entityContainer,
                    targetKey.EntitySetName, (AssociationType)(targetMember.DeclaringType), targetMember.Name, out targetEntitySet); 
                Debug.Assert(associationSet != null, "associationSet should not be null");

                ObjectStateManager manager = Context.ObjectStateManager;
                EntityState newEntryState; 
                // If there is an existing relationship entry, update it based on its current state and the MergeOption, otherwise add a new one
                if (!ObjectStateManager.TryUpdateExistingRelationships(this.Context, this.MergeOption, associationSet, sourceMember, sourceKey, sourceEntity, targetMember, targetKey, /*setIsLoaded*/ true, out newEntryState)) 
                { 
                    // Try to find a state entry for the target key
                    ObjectStateEntry targetEntry = null; 
                    if (!manager.TryGetObjectStateEntry(targetKey, out targetEntry))
                    {
                        // no entry exists for the target key
                        // create a key entry for the target 
                        targetEntry = manager.AddKeyEntry(targetKey, targetEntitySet);
                    } 
 
                    // SQLBU 557105. For 1-1 relationships we have to take care of the relationships of targetEntity
                    bool needNewRelationship = true; 
                    switch (sourceMember.RelationshipMultiplicity)
                    {
                        case RelationshipMultiplicity.ZeroOrOne:
                        case RelationshipMultiplicity.One: 
                            // devnote: targetEntry can be a key entry (targetEntry.Entity == null),
                            // but it that case this parameter won't be used in TryUpdateExistingRelationships 
                            needNewRelationship = !ObjectStateManager.TryUpdateExistingRelationships(this.Context, 
                                this.MergeOption,
                                associationSet, 
                                targetMember,
                                targetKey,
                                targetEntry.Entity as IEntityWithRelationships,
                                sourceMember, 
                                sourceKey,
                                /*setIsLoaded*/ true, 
                                out newEntryState); 

                            // It is possible that as part of removing existing relationships, the key entry was deleted 
                            // If that is the case, recreate the key entry
                            if (targetEntry.State == EntityState.Detached)
                            {
                                targetEntry = manager.AddKeyEntry(targetKey, targetEntitySet); 
                            }
                            break; 
                        case RelationshipMultiplicity.Many: 
                            // we always need a new relationship with Many-To-Many, if there was no exact match between these two entities, so do nothing
                            break; 
                        default:
                            Debug.Assert(false, "Unexpected sourceMember.RelationshipMultiplicity");
                            break;
                    } 

                    if (needNewRelationship) 
                    { 

                        // If the target entry is a key entry, then we need to add a relation 
                        //   between the source and target entries
                        // If we are in a state where we just need to add a new Deleted relation, we
                        //   only need to do that and not touch the related ends
                        // If the target entry is a full entity entry, then we need to add 
                        //   the target entity to the source collection or reference
                        if (targetEntry.IsKeyEntry || newEntryState == EntityState.Deleted) 
                        { 
                            // Add a relationship between the source entity and the target key entry
                            RelationshipWrapper wrapper = new RelationshipWrapper(associationSet, sourceMember.Name, sourceKey, targetMember.Name, targetKey); 
                            manager.AddNewRelation(wrapper, newEntryState);
                        }
                        else
                        { 
                            Debug.Assert(!targetEntry.IsRelationship, "how IsRelationship?");
                            if (targetEntry.State != EntityState.Deleted) 
                            { 
                                // The entry contains an entity, do collection or reference fixup
                                // This will also try to create a new relationship entry or will revert the delete on an existing deleted relationship 
                                ObjectStateManager.AddEntityToCollectionOrReference(
                                    this.MergeOption, sourceEntity, sourceMember,
                                    targetEntry.Entity as IEntityWithRelationships,
                                    targetMember, 
                                    /*setIsLoaded*/ true,
                                    /*relationshipAlreadyExists*/ false, 
                                    /* inKeyEntryPromotion */ false); 
                            }
                            else 
                            {
                                // if the target entry is deleted, then the materializer needs to create a deleted relationship
                                // between the entity and the target entry so that if the entity is deleted, the update
                                // pipeline can find the relationship (even though it is deleted) 
                                RelationshipWrapper wrapper = new RelationshipWrapper(associationSet, sourceMember.Name, sourceKey, targetMember.Name, targetKey);
                                manager.AddNewRelation(wrapper, EntityState.Deleted); 
                            } 
                        }
                    } 
                }
            }
            else
            { 
                IRelatedEnd relatedEnd;
                if(sourceEntity.RelationshipManager.TryGetRelatedEnd(sourceMember.DeclaringType.FullName, targetMember.Name, out relatedEnd)) 
                { 
                    SetIsLoadedForSpan((RelatedEnd)relatedEnd, false);
                } 
            }

            // else there is nothing else for us to do, the relationship has been handled already
            return entity; 
        }
 
        ///  
        /// Sets the IsLoaded flag to "true"
        /// There are also rules for when this can be set based on MergeOption and the current value(s) in the related end. 
        /// 
        private void SetIsLoadedForSpan(RelatedEnd relatedEnd, bool forceToTrue)
        {
            Debug.Assert(relatedEnd != null, "RelatedEnd should not be null"); 

            // We can now say this related end is "Loaded" 
            // The cases where we should set this to true are: 
            // AppendOnly: the related end is empty and does not point to a stub
            // PreserveChanges: the related end is empty and does not point to a stub (otherwise, an Added item exists and IsLoaded should not change) 
            // OverwriteChanges: always
            // NoTracking: always
            if (!forceToTrue)
            { 
                // Detect the empty value state of the relatedEnd
                forceToTrue = relatedEnd.IsEmpty(); 
                EntityReference reference = relatedEnd as EntityReference; 
                if (reference != null)
                { 
                    forceToTrue &= reference.EntityKey == null;
                }
            }
            if (forceToTrue || this.MergeOption == MergeOption.OverwriteChanges) 
            {
                relatedEnd.SetIsLoaded(true); 
            } 
        }
 
        /// 
        /// REQUIRES:: entity is not null; entity set may be null.
        /// Sets up RelationshipManager on IEntityWithRelationships instance. Returns the input
        /// entity so that the call can be composed within a ShaperEmitter Expression delegate. 
        /// 
        public TEntity HandleIEntityWithRelationships(TEntity entity, EntitySet entitySet) 
            where TEntity : IEntityWithRelationships 
        {
            Debug.Assert(null != entity, "entity null"); 
            if (entitySet != null)
            {
                EntityUtil.AttachContext(entity, this.Context, entitySet, this.MergeOption == MergeOption.NoTracking ? MergeOption.NoTracking : MergeOption.AppendOnly);
            } 
            return entity;
        } 
 
        /// 
        /// REQUIRES:: entity is not null and MergeOption is OverwriteChanges or PreserveChanges 
        /// Calls through to HandleEntity after retrieving the EntityKey from the given entity.
        /// 
        public TEntity HandleIEntityWithKey(TEntity entity, EntitySet entitySet)
            where TEntity : IEntityWithKey 
        {
            return HandleEntity(entity, entity.EntityKey, entitySet); 
        } 

        ///  
        /// Calls through to the specified RecordState to set the value for the specified column ordinal.
        /// 
        public bool SetColumnValue(int recordStateSlotNumber, int ordinal, object value)
        { 
            RecordState recordState = (RecordState)this.State[recordStateSlotNumber];
            recordState.SetColumnValue(ordinal, value); 
            return true;  // TRICKY: return true so we can use BitwiseOr expressions to string these guys together. 
        }
 
        /// 
        /// Calls through to the specified RecordState to set the value for the EntityRecordInfo.
        /// 
        public bool SetEntityRecordInfo(int recordStateSlotNumber, EntityKey entityKey, EntitySet entitySet) 
        {
            RecordState recordState = (RecordState)this.State[recordStateSlotNumber]; 
            recordState.SetEntityRecordInfo(entityKey, entitySet); 
            return true;  // TRICKY: return true so we can use BitwiseOr expressions to string these guys together.
        } 

        /// 
        /// REQUIRES:: should be called only by delegate allocating this state.
        /// Utility method assigning a value to a state slot. Returns an arbitrary value 
        /// allowing the method call to be composed in a ShapeEmitter Expression delegate.
        ///  
        public bool SetState(int ordinal, T value) 
        {
            this.State[ordinal] = value; 
            return true;  // TRICKY: return true so we can use BitwiseOr expressions to string these guys together.
        }

        ///  
        /// REQUIRES:: should be called only by delegate allocating this state.
        /// Utility method assigning a value to a state slot and return the value, allowing 
        /// the value to be accessed/set in a ShapeEmitter Expression delegate and later 
        /// retrieved.
        ///  
        public T SetStatePassthrough(int ordinal, T value)
        {
            this.State[ordinal] = value;
            return value; 
        }
 
        ///  
        /// Used to retrieve a property value with exception handling. Normally compiled
        /// delegates directly call typed methods on the DbDataReader (e.g. GetInt32) 
        /// but when an exception occurs we retry using this method to potentially get
        /// a more useful error message to the user.
        /// 
        public TProperty GetPropertyValueWithErrorHandling(int ordinal, string propertyName, string typeName) 
        {
            TProperty result = new PropertyErrorHandlingValueReader(propertyName, typeName).GetValue(this.Reader, ordinal); 
            return result; 
        }
 
        /// 
        /// Used to retrieve a column value with exception handling. Normally compiled
        /// delegates directly call typed methods on the DbDataReader (e.g. GetInt32)
        /// but when an exception occurs we retry using this method to potentially get 
        /// a more useful error message to the user.
        ///  
        public TColumn GetColumnValueWithErrorHandling(int ordinal) 
        {
            TColumn result = new ColumnErrorHandlingValueReader().GetValue(this.Reader, ordinal); 
            return result;
        }

        #endregion 

        #region helper methods (used by runtime callable code) 
 
        private void CheckClearedEntryOnSpan(object targetValue, IEntityWithRelationships sourceEntity, EntityKey sourceKey, AssociationEndMember targetMember)
        { 
            // If a relationship does not exist on the server but does exist on the client,
            // we may need to remove it, depending on the current state and the MergeOption
            if ((null != (object)sourceKey) && (null == targetValue) &&
                (this.MergeOption == MergeOption.PreserveChanges || 
                 this.MergeOption == MergeOption.OverwriteChanges))
            { 
                // When the spanned value is null, it may be because the spanned association applies to a 
                // subtype of the entity's type, and the entity is not actually an instance of that type.
                AssociationEndMember sourceEnd = MetadataHelper.GetOtherAssociationEnd(targetMember); 
                EdmType expectedSourceType = ((RefType)sourceEnd.TypeUsage.EdmType).ElementType;
                TypeUsage entityTypeUsage;
                if (!this.Context.Perspective.TryGetType(sourceEntity.GetType(), out entityTypeUsage) ||
                    entityTypeUsage.EdmType.EdmEquals(expectedSourceType) || 
                    TypeSemantics.IsSubTypeOf(entityTypeUsage.EdmType, expectedSourceType))
                { 
                    // Otherwise, the source entity is the correct type (exactly or a subtype) for the source 
                    // end of the spanned association, so validate that the relationhip that was spanned is
                    // part of the Container owning the EntitySet of the root entity. 
                    // This can be done by comparing the EntitySet  of the row's entity to the relationships
                    // in the Container and their AssociationSetEnd's type
                    CheckClearedEntryOnSpan(sourceKey, sourceEntity, targetMember);
                } 
            }
        } 
 
        private void CheckClearedEntryOnSpan(EntityKey sourceKey, IEntityWithRelationships sourceEntity, AssociationEndMember targetMember)
        { 
            Debug.Assert(null != (object)sourceKey);
            Debug.Assert(sourceEntity != null);
            Debug.Assert(targetMember != null);
            Debug.Assert(this.Context != null); 

            AssociationEndMember sourceMember = MetadataHelper.GetOtherAssociationEnd(targetMember); 
 
            EntityContainer entityContainer = this.Context.MetadataWorkspace.GetEntityContainer(sourceKey.EntityContainerName,
                DataSpace.CSpace); 
            EntitySet sourceEntitySet;
            AssociationSet associationSet = MetadataHelper.GetAssociationsForEntitySetAndAssociationType(entityContainer, sourceKey.EntitySetName,
                (AssociationType)sourceMember.DeclaringType, sourceMember.Name, out sourceEntitySet);
 
            if (associationSet != null)
            { 
                Debug.Assert(associationSet.AssociationSetEnds[sourceMember.Name].EntitySet == sourceEntitySet); 
                ObjectStateManager.RemoveRelationships(Context, MergeOption, associationSet, sourceKey, sourceMember);
 

            }
        }
 
        /// 
        /// Wire's one or more full-spanned entities into the state manager; used by 
        /// both full-spanned collections and full-spanned entities. 
        /// 
        private void FullSpanAction(IEntityWithRelationships sourceEntity, IList spannedEntities, AssociationEndMember targetMember) 
        {
            EntityUtil.CheckKeyForRelationship(sourceEntity, MergeOption);
            EntityKey sourceKey = ObjectStateManager.FindKeyOnEntityWithRelationships(sourceEntity);
 
            if (sourceEntity != null)
            { 
                IRelatedEnd relatedEnd; 
                if(sourceEntity.RelationshipManager.TryGetRelatedEnd(targetMember.DeclaringType.FullName, targetMember.Name, out relatedEnd))
                { 
                    // Add members of the list to the source entity (item in column 0)
                    AssociationEndMember sourceMember = MetadataHelper.GetOtherAssociationEnd(targetMember);

                    int count = ObjectStateManager.UpdateRelationships(this.Context, this.MergeOption, (AssociationSet)relatedEnd.RelationshipSet, sourceMember, sourceKey, sourceEntity, targetMember, (List)spannedEntities, true); 

                    SetIsLoadedForSpan((RelatedEnd)relatedEnd, count > 0); 
                } 
            }
        } 

        #region update existing ObjectStateEntry

        private void UpdateEntry(TEntity entity, ObjectStateEntry existingEntry) 
        {
            Debug.Assert(null != entity, "null entity"); 
            Debug.Assert(null != existingEntry, "null ObjectStateEntry"); 
            Debug.Assert(null != existingEntry.Entity, "ObjectStateEntry without Entity");
 
            Type clrType = typeof(TEntity);
            if (clrType != existingEntry.Entity.GetType())
            {
                throw EntityUtil.RecyclingEntity(existingEntry.EntityKey, clrType, existingEntry.Entity.GetType()); 
            }
 
            if (EntityState.Added == existingEntry.State) 
            {
                throw EntityUtil.AddedEntityAlreadyExists(existingEntry.EntityKey); 
            }

            if (MergeOption.AppendOnly != MergeOption)
            {   // existing entity, update CSpace values in place 
                Debug.Assert(EntityState.Added != existingEntry.State, "entry in State=Added");
                Debug.Assert(EntityState.Detached != existingEntry.State, "entry in State=Detached"); 
 
                if (MergeOption.OverwriteChanges == MergeOption)
                { 
                    if (EntityState.Deleted == existingEntry.State)
                    {
                        existingEntry.RevertDelete();
                    } 
                    Shaper.UpdateRecord(entity, existingEntry.CurrentValues);
                    existingEntry.AcceptChanges(); 
                } 
                else
                { 
                    Debug.Assert(MergeOption.PreserveChanges == MergeOption, "not MergeOption.PreserveChanges");
                    if (EntityState.Unchanged == existingEntry.State)
                    {
                        // same behavior as MergeOption.OverwriteChanges 
                        UpdateRecord(entity, existingEntry.CurrentValues);
                        existingEntry.AcceptChanges(); 
                    } 
                    else
                    { 
                        Shaper.UpdateRecord(entity, existingEntry.EditableOriginalValues);
                    }
                }
            } 
        }
 
        static internal void UpdateRecord(object value, CurrentValueRecord current) 
        {
            Debug.Assert(null != value, "null value"); 
            Debug.Assert(null != current, "null CurrentValueRecord");

            // get Metadata for type
            StateManagerTypeMetadata typeMetadata = current._metadata; 
            DataRecordInfo recordInfo = typeMetadata.DataRecordInfo;
            IBaseList structure = TypeHelpers.GetAllStructuralMembers(recordInfo.RecordType); 
            foreach (FieldMetadata field in recordInfo.FieldMetadata) 
            {
                int index = structure.IndexOf(field.FieldType); 
                object fieldValue = typeMetadata.Member(index).GetValue(value) ?? DBNull.Value;

                if (Helper.IsComplexType(field.FieldType.TypeUsage.EdmType))
                { 
                    object existing = current.GetValue(index);
                    // Ensure that the existing ComplexType value is not null. This is not supported. 
                    if (existing == DBNull.Value) 
                    {
                        throw EntityUtil.NullableComplexTypesNotSupported(field.FieldType.Name); 
                    }
                    else if (fieldValue != DBNull.Value)
                    {
                        // There is both an IExtendedDataRecord and an existing CurrentValueRecord 
                        Shaper.UpdateRecord(fieldValue, (CurrentValueRecord)existing);
                    } 
                } 
                else
                { 
                    Debug.Assert(Helper.IsPrimitiveType(field.FieldType.TypeUsage.EdmType),
                                 "Property is not PrimitiveType");

                    object existing = current.GetValue(index) ?? DBNull.Value; 
                    if ((existing != fieldValue) &&
                        (((object)DBNull.Value == fieldValue) || 
                         ((object)DBNull.Value == existing) || 
                         (!existing.Equals(fieldValue))))
                    { 
                        current.SetValue(index, fieldValue);
                    }
                }
            } 
        }
 
        #endregion 

        #endregion 

        #region nested types
        private abstract class ErrorHandlingValueReader
        { 
            /// 
            /// Gets value from reader using the same pattern as the materializer delegate. Avoids 
            /// the need to compile multiple delegates for error handling. If there is a failure 
            /// reading a value
            ///  
            internal T GetValue(DbDataReader reader, int ordinal)
            {
                T result;
                bool isNullable; 
                MethodInfo readerMethod = Translator.GetReaderMethod(typeof(T), out isNullable);
                if (reader.IsDBNull(ordinal)) 
                { 
                    try
                    { 
                        result = (T)(object)null;
                    }
                    catch (NullReferenceException)
                    { 
                        // NullReferenceException is thrown when casting null to a value type.
                        // We don't use isNullable here because of an issue with GetReaderMethod 
                        // 
                        throw CreateNullValueException();
                    } 
                }
                else
                {
                    try 
                    {
                        // use the specific reader.GetXXX method 
                        result = (T)readerMethod.Invoke(reader, new object[] { ordinal }); 
                    }
                    catch (Exception e) 
                    {
                        if (EntityUtil.IsCatchableExceptionType(e))
                        {
                            // determine if the problem is with the result type 
                            // (note that if we throw on this call, it's ok
                            // for it to percolate up -- we only intercept type 
                            // and null mismatches) 
                            object untypedResult = reader.GetValue(ordinal);
                            Type resultType = null == untypedResult ? null : untypedResult.GetType(); 
                            if (!typeof(T).IsAssignableFrom(resultType))
                            {
                                throw CreateWrongTypeException(resultType);
                            } 
                        }
                        throw; 
                    } 
                }
                return result; 
            }

            /// 
            /// Creates the exception thrown when the reader returns a null value 
            /// for a non nullable property/column.
            ///  
            protected abstract Exception CreateNullValueException(); 

            ///  
            /// Creates the exception thrown when the reader returns a value with
            /// an incompatible type.
            /// 
            protected abstract Exception CreateWrongTypeException(Type resultType); 
        }
 
        private class ColumnErrorHandlingValueReader : ErrorHandlingValueReader 
        {
            internal ColumnErrorHandlingValueReader() 
            {
            }

            protected override Exception CreateNullValueException() 
            {
                return EntityUtil.ValueNullReferenceCast(typeof(TColumn)); 
            } 

            protected override Exception  CreateWrongTypeException(Type resultType) 
            {
                return EntityUtil.ValueInvalidCast(resultType, typeof(TColumn));
            }
        } 

        private class PropertyErrorHandlingValueReader : ErrorHandlingValueReader 
        { 
            private readonly string _propertyName;
            private readonly string _typeName; 

            internal PropertyErrorHandlingValueReader(string propertyName, string typeName)
            {
                _propertyName = propertyName; 
                _typeName = typeName;
            } 
 
            protected override Exception CreateNullValueException()
            { 
                return EntityUtil.Constraint(
                                        System.Data.Entity.Strings.Materializer_SetInvalidValue(
                                        (Nullable.GetUnderlyingType(typeof(TProperty)) ?? typeof(TProperty)).Name,
                                        _typeName, _propertyName, "null")); 
            }
 
            protected override Exception CreateWrongTypeException(Type resultType) 
            {
                return EntityUtil.InvalidOperation( 
                                        System.Data.Entity.Strings.Materializer_SetInvalidValue(
                                        (Nullable.GetUnderlyingType(typeof(TProperty)) ?? typeof(TProperty)).Name,
                                        _typeName, _propertyName, resultType.Name));
            } 
        }
        #endregion 
    } 

    ///  
    /// Typed Shaper. Includes logic to enumerate results and wraps the _rootCoordinator,
    /// which includes materializer delegates for the root query collection.
    /// 
    internal sealed class Shaper : Shaper 
    {
        #region private state 
 
        /// 
        /// Shapers and Coordinators work together in harmony to materialize the data 
        /// from the store; the shaper contains the state, the coordinator contains the
        /// code.
        /// 
        internal readonly Coordinator RootCoordinator; 

        ///  
        /// What we need to call when we read to ensure we're maintaining security; will 
        /// do the approprate security demands.
        ///  
        private readonly Action CheckPermissionsAction;

        /// 
        /// Which type of query is this, object layer (true) or value layer (false) 
        /// 
        private readonly bool IsObjectQuery; 
 
        /// 
        /// Keeps track of whether we've completed processing or not. 
        /// 
        private bool _isActive;

        ///  
        /// The enumerator we're using to read data; really only populated for value
        /// layer queries. 
        ///  
        private IEnumerator _rootEnumerator;
 
        /// 
        /// Whether the current value of _rootEnumerator has been returned by a bridge
        /// data reader.
        ///  
        private bool _dataWaiting;
 
        #endregion 

        #region constructor 

        internal Shaper(DbDataReader reader, ObjectContext context, MetadataWorkspace workspace, MergeOption mergeOption, int stateCount, CoordinatorFactory rootCoordinatorFactory, Action checkPermissions)
            : base(reader, context, workspace, mergeOption, stateCount)
        { 
            RootCoordinator = new Coordinator(rootCoordinatorFactory, /*parent*/ null, /*next*/ null);
            CheckPermissionsAction = checkPermissions; 
            IsObjectQuery = !(typeof(T) == typeof(RecordState)); 
            _isActive = true;
            RootCoordinator.Initialize(this); 
        }

        #endregion
 
        #region "public" surface area
 
        ///  
        /// Events raised when the shaper has finished enumerating results. Useful for callback
        /// to set parameter values. 
        /// 
        internal event EventHandler OnDone;

        ///  
        /// Used to handle the read-ahead requirements of value-layer queries.  This
        /// field indicates the status of the current value of the _rootEnumerator; when 
        /// a bridge data reader "accepts responsibility" for the current value, it sets 
        /// this to false.
        ///  
        internal bool DataWaiting
        {
            get { return _dataWaiting; }
            set { _dataWaiting = value; } 
        }
 
        ///  
        /// The enumerator that the value-layer bridge will use to read data; all nested
        /// data readers need to use the same enumerator, so we put it on the Shaper, since 
        /// that is something that all the nested data readers (and data records) have access
        /// to -- it prevents us from having to pass two objects around.
        /// 
        internal IEnumerator RootEnumerator 
        {
            get 
            { 
                if (_rootEnumerator == null)
                { 
                    InitializeRecordStates(RootCoordinator.CoordinatorFactory);
                    _rootEnumerator = GetEnumerator();
                }
                return _rootEnumerator; 
            }
        } 
 
        /// 
        /// Initialize the RecordStateFactory objects in their StateSlots. 
        /// 
        private void InitializeRecordStates(CoordinatorFactory coordinatorFactory)
        {
            foreach (RecordStateFactory recordStateFactory in coordinatorFactory.RecordStateFactories) 
            {
                State[recordStateFactory.StateSlotNumber] = recordStateFactory.Create(coordinatorFactory); 
            } 

            foreach (CoordinatorFactory nestedCoordinatorFactory in coordinatorFactory.NestedCoordinators) 
            {
                InitializeRecordStates(nestedCoordinatorFactory);
            }
        } 

        public IEnumerator GetEnumerator() 
        { 
            // we can use a simple enumerator if there are no nested results, no keys and no "has data"
            // discriminator 
            if (RootCoordinator.CoordinatorFactory.IsSimple)
            {
                return new SimpleEnumerator(this);
            } 
            else
            { 
                RowNestedResultEnumerator rowEnumerator = new Shaper.RowNestedResultEnumerator(this); 

                if (this.IsObjectQuery) 
                {
                    return new ObjectQueryNestedEnumerator(rowEnumerator);
                }
                else 
                {
                    return (IEnumerator)(object)(new RecordStateEnumerator(rowEnumerator)); 
                } 
            }
        } 

        #endregion

        #region enumerator helpers 

        ///  
        /// Called when enumeration of results has completed. 
        /// 
        private void Finally() 
        {
            if (_isActive)
            {
                _isActive = false; 

                // I'd prefer not to special case this, but value-layer behavior is that you 
                // must explicitly close the data reader; if we automatically dispose of the 
                // reader here, we won't have that behavior.
                if (IsObjectQuery) 
                {
                    this.Reader.Dispose();
                }
 
                // This case includes when the ObjectResult is disposed before it
                // created an ObjectQueryEnumeration; at this time, the connection can be released 
                if (this.Context != null) 
                {
                    this.Context.ReleaseConnection(); 
                }

                if (null != this.OnDone)
                { 
                    this.OnDone(this, new EventArgs());
                } 
            } 
        }
 
        /// 
        /// Reads the next row from the store. If there is a failure, throws an exception message
        /// in some scenarios (note that we respond to failure rather than anticipate failure,
        /// avoiding repeated checks in the inner materialization loop) 
        /// 
        private bool StoreRead() 
        { 
            try
            { 
                return this.Reader.Read();
            }
            catch (Exception e)
            { 
                // check if the reader is closed; if so, throw friendlier exception
                if (this.Reader.IsClosed) 
                { 
                    const string operation = "Read";
                    throw EntityUtil.DataReaderClosed(operation); 
                }

                // wrap exception if necessary
                if (EntityUtil.IsCatchableEntityExceptionType(e)) 
                {
                    throw EntityUtil.CommandExecution(System.Data.Entity.Strings.EntityClient_StoreReaderFailed, e); 
                } 
                throw;
            } 
        }

        #endregion
 
        #region simple enumerator
 
        ///  
        /// Optimized enumerator for queries not including nested results.
        ///  
        private class SimpleEnumerator : IEnumerator
        {
            private readonly Shaper _shaper;
 
            internal SimpleEnumerator(Shaper shaper)
            { 
                _shaper = shaper; 
            }
 
            public T Current
            {
                get { return _shaper.RootCoordinator.Current; }
            } 

            object System.Collections.IEnumerator.Current 
            { 
                get { return _shaper.RootCoordinator.Current; }
            } 

            public void Dispose()
            {
                // For backwards compatibility, we set the current value to the 
                // default value, so you can still call Current.
                _shaper.RootCoordinator.SetCurrentToDefault(); 
                _shaper.Finally(); 
            }
 
            public bool MoveNext()
            {
                if (!_shaper._isActive)
                { 
                    return false;
                } 
                if (_shaper.StoreRead()) 
                {
                    if (null != _shaper.CheckPermissionsAction) 
                    {
                        _shaper.CheckPermissionsAction();
                    }
                    _shaper.RootCoordinator.ReadNextElement(_shaper); 
                    return true;
                } 
                this.Dispose(); 
                return false;
            } 

            public void Reset()
            {
                throw EntityUtil.NotSupported(); 
            }
        } 
 
        #endregion
 
        #region nested enumerator

        /// 
        /// Enumerates (for each row in the input) an array of all coordinators producing new elements. The array 
        /// contains a position for each 'depth' in the result. A null value in any position indicates that no new
        /// results were produced for the given row at the given depth. It is possible for a row to contain no 
        /// results for any row. 
        /// 
        private class RowNestedResultEnumerator : IEnumerator 
        {
            private readonly Shaper _shaper;
            private readonly Coordinator[] _current;
 
            internal RowNestedResultEnumerator(Shaper shaper)
            { 
                _shaper = shaper; 
                _current = new Coordinator[_shaper.RootCoordinator.MaxDistanceToLeaf() + 1];
            } 

            public Coordinator[] Current
            {
                get { return _current; } 
            }
 
            public void Dispose() 
            {
                _shaper.Finally(); 
            }

            object System.Collections.IEnumerator.Current
            { 
                get { return _current; }
            } 
 
            public bool MoveNext()
            { 
                Coordinator currentCoordinator = _shaper.RootCoordinator;

                if (!_shaper.StoreRead())
                { 
                    // Reset all collections
                    this.RootCoordinator.ResetCollection(_shaper); 
                    return false; 
                }
 
                int depth = 0;
                bool haveInitializedChildren = false;
                for (; depth < _current.Length; depth++)
                { 
                    // find a coordinator at this depth that currently has data (if any)
                    while (currentCoordinator != null && !currentCoordinator.CoordinatorFactory.HasData(_shaper)) 
                    { 
                        currentCoordinator = currentCoordinator.Next;
                    } 
                    if (null == currentCoordinator)
                    {
                        break;
                    } 

                    // check if this row contains a new element for this coordinator 
                    if (currentCoordinator.HasNextElement(_shaper)) 
                    {
                        // if we have children and haven't initialized them yet, do so now 
                        if (!haveInitializedChildren && null != currentCoordinator.Child)
                        {
                            currentCoordinator.Child.ResetCollection(_shaper);
                        } 
                        haveInitializedChildren = true;
 
                        // read the next element 
                        currentCoordinator.ReadNextElement(_shaper);
 
                        // place the coordinator in the result array to indicate there is a new
                        // element at this depth
                        _current[depth] = currentCoordinator;
                    } 
                    else
                    { 
                        // clear out the coordinator in result array to indicate there is no new 
                        // element at this depth
                        _current[depth] = null; 
                    }

                    // move to child (in the next iteration we deal with depth + 1
                    currentCoordinator = currentCoordinator.Child; 
                }
 
                // clear out all positions below the depth we reached before we ran out of data 
                for (; depth < _current.Length; depth++)
                { 
                    _current[depth] = null;
                }

                return true; 
            }
 
            public void Reset() 
            {
                throw EntityUtil.NotSupported(); 
            }

            internal Coordinator RootCoordinator
            { 
                get { return _shaper.RootCoordinator; }
            } 
        } 

        ///  
        /// Wraps RowNestedResultEnumerator and yields results appropriate to an ObjectQuery instance. In particular,
        /// root level elements (T) are returned only after aggregating all child elements.
        /// 
        private class ObjectQueryNestedEnumerator : IEnumerator 
        {
            private readonly RowNestedResultEnumerator _rowEnumerator; 
            private T _previousElement; 
            private State _state;
 
            internal ObjectQueryNestedEnumerator(RowNestedResultEnumerator rowEnumerator)
            {
                _rowEnumerator = rowEnumerator;
                _previousElement = default(T); 
                _state = State.Start;
            } 
 
            public T Current { get { return _previousElement; } }
 
            public void Dispose()
            {
                _rowEnumerator.Dispose();
            } 

            object System.Collections.IEnumerator.Current { get { return this.Current; } } 
 
            public bool MoveNext()
            { 
                // See the documentation for enum State to understand the behaviors and requirements
                // for each state.
                switch (_state)
                { 
                    case State.Start:
                        { 
                            if (TryReadToNextElement()) 
                            {
                                // if there's an element in the reader... 
                                ReadElement();
                            }
                            else
                            { 
                                // no data at all...
                                _state = State.NoRows; 
                            } 
                        };
                        break; 
                    case State.Reading:
                        {
                            ReadElement();
                        }; 
                        break;
                    case State.NoRowsLastElementPending: 
                        { 
                            // nothing to do but move to the next state...
                            _state = State.NoRows; 
                        };
                        break;
                }
 
                bool result;
                if (_state == State.NoRows) 
                { 
                    _previousElement = default(T);
                    result = false; 
                }
                else
                {
                    result = true; 
                }
 
                return result; 
            }
 
            /// 
            /// Requires: the row is currently positioned at the start of an element.
            ///
            /// Reads all rows in the element and sets up state for the next element (if any). 
            /// 
            private void ReadElement() 
            { 
                // remember the element we're currently reading
                _previousElement = _rowEnumerator.RootCoordinator.Current; 

                // now we need to read to the next element (or the end of the
                // reader) so that we can return the first element
                if (TryReadToNextElement()) 
                {
                    // we're positioned at the start of the next element (which 
                    // corresponds to the 'reading' state) 
                    _state = State.Reading;
                } 
                else
                {
                    // we're positioned at the end of the reader
                    _state = State.NoRowsLastElementPending; 
                }
            } 
 
            /// 
            /// Reads rows until the start of a new element is found. If no element 
            /// is found before all rows are consumed, returns false.
            /// 
            private bool TryReadToNextElement()
            { 
                bool result = false;
                while (_rowEnumerator.MoveNext()) 
                { 
                    // if we hit a new element, return true
                    if (_rowEnumerator.Current[0] != null) 
                    {
                        result = true;
                        break;
                    } 
                }
                return result; 
            } 

            public void Reset() 
            {
                _rowEnumerator.Reset();
            }
 
            /// 
            /// Describes the state of this enumerator with respect to the _rowEnumerator 
            /// it wraps. 
            /// 
            private enum State 
            {
                /// 
                /// No rows have been read yet
                ///  
                Start,
 
                ///  
                /// Positioned at the start of a new root element. The previous element must
                /// be stored in _previousElement. We read ahead in this manner so that 
                /// the previous element is fully populated (all of its children loaded)
                /// before returning.
                /// 
                Reading, 

                ///  
                /// Positioned past the end of the rows. The last element in the enumeration 
                /// has not yet been returned to the user however, and is stored in _previousElement.
                ///  
                NoRowsLastElementPending,

                /// 
                /// Positioned past the end of the rows. The last element has been returned to 
                /// the user.
                ///  
                NoRows, 
            }
        } 

        /// 
        /// Wraps RowNestedResultEnumerator and yields results appropriate to an EntityReader instance. In particular,
        /// yields RecordState whenever a new element becomes available at any depth in the result hierarchy. 
        /// 
        private class RecordStateEnumerator : IEnumerator 
        { 
            private readonly RowNestedResultEnumerator _rowEnumerator;
            private RecordState _current; 

            /// 
            /// Gets depth of coordinator we're currently consuming. If _depth == -1, it means we haven't started
            /// to consume the next row yet. 
            /// 
            private int _depth; 
            private bool _readerConsumed; 

            internal RecordStateEnumerator(RowNestedResultEnumerator rowEnumerator) 
            {
                _rowEnumerator = rowEnumerator;
                _current = null;
                _depth = -1; 
                _readerConsumed = false;
            } 
 
            public RecordState Current
            { 
                get { return _current; }
            }

            public void Dispose() 
            {
                _rowEnumerator.Dispose(); 
            } 

            object System.Collections.IEnumerator.Current 
            {
                get { return _current; }
            }
 
            public bool MoveNext()
            { 
                if (!_readerConsumed) 
                {
                    while (true) 
                    {
                        // keep on cycling until we find a result
                        if (-1 == _depth || _rowEnumerator.Current.Length == _depth)
                        { 
                            // time to move to the next row...
                            if (!_rowEnumerator.MoveNext()) 
                            { 
                                // no more rows...
                                _current = null; 
                                _readerConsumed = true;
                                break;
                            }
 
                            _depth = 0;
                        } 
 
                        // check for results at the current depth
                        Coordinator currentCoordinator = _rowEnumerator.Current[_depth]; 
                        if (null != currentCoordinator)
                        {
                            _current = ((Coordinator)currentCoordinator).Current;
                            _depth++; 
                            break;
                        } 
 
                        _depth++;
                    } 
                }

                return !_readerConsumed;
            } 

            public void Reset() 
            { 
                _rowEnumerator.Reset();
            } 
        }

        #endregion
 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
// [....]
//----------------------------------------------------------------------------- 
 
using System.Collections.Generic;
using System.Data.Common.Utils; 
using System.Linq;
using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Data.Objects.DataClasses; 
using System.Diagnostics;
using System.Reflection; 
 
namespace System.Data.Common.Internal.Materialization
{ 
    /// 
    /// Shapes store reader values into EntityClient/ObjectQuery results. Also maintains
    /// state used by materializer delegates.
    ///  
    internal abstract class Shaper
    { 
        #region constructor 

        internal Shaper(DbDataReader reader, ObjectContext context, MetadataWorkspace workspace, MergeOption mergeOption, int stateCount) 
        {
            Debug.Assert(context == null || workspace == context.MetadataWorkspace, "workspace must match context's workspace");

            this.Reader = reader; 
            this.MergeOption = mergeOption;
            this.State = new object[stateCount]; 
            this.Context = context; 
            this.Workspace = workspace;
        } 

        #endregion

        #region runtime callable/accessible code 

        // Code in this section is called from the delegates produced by the Translator.  It 
        // may not show up if you search using Find All References...use Find in Files instead. 
        //
        // Many items on this class are public, simply to make the job of producing the 
        // expressions that use them simpler.  If you have a hankering to make them private,
        // you will need to modify the code in the Translator that does the GetMethod/GetField
        // to use BindingFlags.NonPublic | BindingFlags.Instance as well.
        // 
        // Debug.Asserts that fire from the code in this region will probably create a
        // SecurityException in the Coordinator's Read method since those are restricted when 
        // running the Shaper. 

        ///  
        /// The store data reader we're pulling data from
        /// 
        public readonly DbDataReader Reader;
 
        /// 
        /// The state slots we use in the coordinator expression. 
        ///  
        public readonly object[] State;
 
        /// 
        /// The context the shaper is performing for.
        /// 
        public readonly ObjectContext Context; 

        ///  
        /// The workspace we are performing for; yes we could get it from the context, but 
        /// it's much easier to just have it handy.
        ///  
        public readonly MetadataWorkspace Workspace;

        /// 
        /// The merge option this shaper is performing under/for. 
        /// 
        public readonly MergeOption MergeOption; 
 
        /// 
        /// Utility method used to evaluate a multi-discriminator column map. Takes 
        /// discriminator values and determines the appropriate entity type, then looks up
        /// the appropriate handler and invokes it.
        /// 
        public TElement Discriminate(object[] discriminatorValues, Func discriminate, KeyValuePair>[] elementDelegates) 
        {
            EntityType entityType = discriminate(discriminatorValues); 
            Func elementDelegate = null; 
            foreach (KeyValuePair> typeDelegatePair in elementDelegates)
            { 
                if (typeDelegatePair.Key == entityType)
                {
                    elementDelegate = typeDelegatePair.Value;
                } 
            }
            return elementDelegate(this); 
        } 

        ///  
        /// REQUIRES:: entity is not null and MergeOption is OverwriteChanges or PreserveChanges
        /// Handles state management for an entity returned by a query. Where an existing entry
        /// exists, updates that entry and returns the existing entity. Otherwise, the entity
        /// passed in is returned. 
        /// 
        public TEntity HandleEntity(TEntity entity, EntityKey entityKey, EntitySet entitySet) 
        { 
            Debug.Assert(MergeOption.NoTracking != this.MergeOption, "no need to HandleEntity if there's no tracking");
            Debug.Assert(MergeOption.AppendOnly != this.MergeOption, "use HandleEntityAppendOnly instead..."); 
            Debug.Assert(null != entity, "if HandleEntity is called, there must be an entity");

            TEntity result = entity;
 
            // no entity set, so no tracking is required for this entity
            if (null != (object)entityKey) 
            { 
                Debug.Assert(null != entitySet, "if there is an entity key, there must also be an entity set");
 
                // check for an existing entity with the same key
                ObjectStateEntry existingEntry = this.Context.ObjectStateManager.FindObjectStateEntry(entityKey);
                if (null != existingEntry && !existingEntry.IsKeyEntry)
                { 
                    Debug.Assert(existingEntry.EntityKey.Equals(entityKey), "Found ObjectStateEntry with wrong EntityKey");
                    UpdateEntry(entity, existingEntry); 
                    result = (TEntity)existingEntry.Entity; 
                }
                else 
                {
                    // if the entity isn't tracked yet, attach it
                    if (null == existingEntry)
                    { 
                        Context.ObjectStateManager.AddEntry(entity, entityKey, entitySet, "HandleEntity", false);
                    } 
                    else 
                    {
                        Context.ObjectStateManager.PromoteKeyEntry(existingEntry, entity, (IExtendedDataRecord)null, false, /*setIsLoaded*/ true, /*keyEntryInitialized*/ false, "HandleEntity"); 
                    }
                }
            }
            return result; 
        }
 
        ///  
        /// REQUIRES:: entity exists; MergeOption is AppendOnly
        /// Handles state management for an entity with the given key. When the entity already exists 
        /// in the state manager, it is returned directly. Otherwise, the entityDelegate is invoked and
        /// the resulting entity is returned.
        /// 
        public TEntity HandleEntityAppendOnly(Func constructEntityDelegate, EntityKey entityKey, EntitySet entitySet) 
        {
            Debug.Assert(this.MergeOption == MergeOption.AppendOnly, "only use HandleEntityAppendOnly when MergeOption is AppendOnly"); 
            Debug.Assert(null != constructEntityDelegate, "must provide delegate to construct the entity"); 

            TEntity result; 

            if (null == (object)entityKey)
            {
                // no entity set, so no tracking is required for this entity, just 
                // call the delegate to "materialize" it.
                result = constructEntityDelegate(this); 
            } 
            else
            { 
                Debug.Assert(null != entitySet, "if there is an entity key, there must also be an entity set");

                // check for an existing entity with the same key
                ObjectStateEntry existingEntry = this.Context.ObjectStateManager.FindObjectStateEntry(entityKey); 
                if (null != existingEntry && !existingEntry.IsKeyEntry)
                { 
                    Debug.Assert(existingEntry.EntityKey.Equals(entityKey), "Found ObjectStateEntry with wrong EntityKey"); 
                    if (typeof(TEntity) != existingEntry.Entity.GetType())
                    { 
                        throw EntityUtil.RecyclingEntity(existingEntry.EntityKey, typeof(TEntity), existingEntry.Entity.GetType());
                    }

                    if (EntityState.Added == existingEntry.State) 
                    {
                        throw EntityUtil.AddedEntityAlreadyExists(existingEntry.EntityKey); 
                    } 
                    result = (TEntity)existingEntry.Entity;
                } 
                else
                {
                    // We don't already have the entity, so construct it and if it isn't
                    // tracked yet, attach it 
                    result = constructEntityDelegate(this);
                    if (null == existingEntry) 
                    { 
                        Context.ObjectStateManager.AddEntry(result, entityKey, entitySet, "HandleEntity", false);
                    } 
                    else
                    {
                        Context.ObjectStateManager.PromoteKeyEntry(existingEntry, result, (IExtendedDataRecord)null, false, /*setIsLoaded*/ true, /*keyEntryInitialized*/ false, "HandleEntity");
                    } 
                }
            } 
            return result; 
        }
 
        /// 
        /// Call to ensure a collection of full-spanned elements are added
        /// into the state manager properly.  We registers an action to be called
        /// when the collection is closed that pulls the collection of full spanned 
        /// objects into the state manager.
        ///  
        public T_SourceEntity HandleFullSpanCollection(T_SourceEntity entity, Coordinator coordinator, AssociationEndMember targetMember) 
        {
            IEntityWithRelationships sourceEntity = entity as IEntityWithRelationships; 
            if (null != sourceEntity)
            {
                coordinator.RegisterCloseHandler((state, spannedEntities) => FullSpanAction(sourceEntity, spannedEntities, targetMember));
            } 
            return entity;
        } 
 
        /// 
        /// Call to ensure a single full-spanned element is added into 
        /// the state manager properly.
        /// 
        public T_SourceEntity HandleFullSpanElement(T_SourceEntity entity, T_TargetEntity spannedEntity, AssociationEndMember targetMember)
        { 
            IEntityWithRelationships sourceEntity = entity as IEntityWithRelationships;
            if (null != sourceEntity) 
            { 
                List spannedEntities = null;
                if (spannedEntity != null) 
                {
                    // There was a single entity in the column
                    // Create a list so we can perform the same logic as a collection of entities
                    spannedEntities = new List(1); 
                    spannedEntities.Add(spannedEntity);
                } 
                else 
                {
                    EntityKey sourceKey = ObjectStateManager.FindKeyOnEntityWithRelationships(sourceEntity); 
                    CheckClearedEntryOnSpan(spannedEntity, sourceEntity, sourceKey, targetMember);
                }
                FullSpanAction(sourceEntity, spannedEntities, targetMember);
            } 
            return entity;
        } 
 
        /// 
        /// Call to ensure a target entities key is added into the state manager 
        /// properly
        /// 
        public T_SourceEntity HandleRelationshipSpan(T_SourceEntity entity, EntityKey targetKey, AssociationEndMember targetMember)
        { 
            IEntityWithRelationships sourceEntity = entity as IEntityWithRelationships;
            if (null == sourceEntity) 
            { 
                return entity;
            } 
            Debug.Assert(targetMember != null);
            Debug.Assert(targetMember.RelationshipMultiplicity == RelationshipMultiplicity.One || targetMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne);

            EntityUtil.CheckKeyForRelationship(sourceEntity, MergeOption); 
            EntityKey sourceKey = ObjectStateManager.FindKeyOnEntityWithRelationships(sourceEntity);
            AssociationEndMember sourceMember = MetadataHelper.GetOtherAssociationEnd(targetMember); 
            CheckClearedEntryOnSpan(targetKey, sourceEntity, sourceKey, targetMember); 

            if (null != (object)targetKey) 
            {
                EntitySet targetEntitySet;

                EntityContainer entityContainer = this.Context.MetadataWorkspace.GetEntityContainer( 
                    targetKey.EntityContainerName, DataSpace.CSpace);
 
                // find the correct AssociationSet 
                AssociationSet associationSet = MetadataHelper.GetAssociationsForEntitySetAndAssociationType(entityContainer,
                    targetKey.EntitySetName, (AssociationType)(targetMember.DeclaringType), targetMember.Name, out targetEntitySet); 
                Debug.Assert(associationSet != null, "associationSet should not be null");

                ObjectStateManager manager = Context.ObjectStateManager;
                EntityState newEntryState; 
                // If there is an existing relationship entry, update it based on its current state and the MergeOption, otherwise add a new one
                if (!ObjectStateManager.TryUpdateExistingRelationships(this.Context, this.MergeOption, associationSet, sourceMember, sourceKey, sourceEntity, targetMember, targetKey, /*setIsLoaded*/ true, out newEntryState)) 
                { 
                    // Try to find a state entry for the target key
                    ObjectStateEntry targetEntry = null; 
                    if (!manager.TryGetObjectStateEntry(targetKey, out targetEntry))
                    {
                        // no entry exists for the target key
                        // create a key entry for the target 
                        targetEntry = manager.AddKeyEntry(targetKey, targetEntitySet);
                    } 
 
                    // SQLBU 557105. For 1-1 relationships we have to take care of the relationships of targetEntity
                    bool needNewRelationship = true; 
                    switch (sourceMember.RelationshipMultiplicity)
                    {
                        case RelationshipMultiplicity.ZeroOrOne:
                        case RelationshipMultiplicity.One: 
                            // devnote: targetEntry can be a key entry (targetEntry.Entity == null),
                            // but it that case this parameter won't be used in TryUpdateExistingRelationships 
                            needNewRelationship = !ObjectStateManager.TryUpdateExistingRelationships(this.Context, 
                                this.MergeOption,
                                associationSet, 
                                targetMember,
                                targetKey,
                                targetEntry.Entity as IEntityWithRelationships,
                                sourceMember, 
                                sourceKey,
                                /*setIsLoaded*/ true, 
                                out newEntryState); 

                            // It is possible that as part of removing existing relationships, the key entry was deleted 
                            // If that is the case, recreate the key entry
                            if (targetEntry.State == EntityState.Detached)
                            {
                                targetEntry = manager.AddKeyEntry(targetKey, targetEntitySet); 
                            }
                            break; 
                        case RelationshipMultiplicity.Many: 
                            // we always need a new relationship with Many-To-Many, if there was no exact match between these two entities, so do nothing
                            break; 
                        default:
                            Debug.Assert(false, "Unexpected sourceMember.RelationshipMultiplicity");
                            break;
                    } 

                    if (needNewRelationship) 
                    { 

                        // If the target entry is a key entry, then we need to add a relation 
                        //   between the source and target entries
                        // If we are in a state where we just need to add a new Deleted relation, we
                        //   only need to do that and not touch the related ends
                        // If the target entry is a full entity entry, then we need to add 
                        //   the target entity to the source collection or reference
                        if (targetEntry.IsKeyEntry || newEntryState == EntityState.Deleted) 
                        { 
                            // Add a relationship between the source entity and the target key entry
                            RelationshipWrapper wrapper = new RelationshipWrapper(associationSet, sourceMember.Name, sourceKey, targetMember.Name, targetKey); 
                            manager.AddNewRelation(wrapper, newEntryState);
                        }
                        else
                        { 
                            Debug.Assert(!targetEntry.IsRelationship, "how IsRelationship?");
                            if (targetEntry.State != EntityState.Deleted) 
                            { 
                                // The entry contains an entity, do collection or reference fixup
                                // This will also try to create a new relationship entry or will revert the delete on an existing deleted relationship 
                                ObjectStateManager.AddEntityToCollectionOrReference(
                                    this.MergeOption, sourceEntity, sourceMember,
                                    targetEntry.Entity as IEntityWithRelationships,
                                    targetMember, 
                                    /*setIsLoaded*/ true,
                                    /*relationshipAlreadyExists*/ false, 
                                    /* inKeyEntryPromotion */ false); 
                            }
                            else 
                            {
                                // if the target entry is deleted, then the materializer needs to create a deleted relationship
                                // between the entity and the target entry so that if the entity is deleted, the update
                                // pipeline can find the relationship (even though it is deleted) 
                                RelationshipWrapper wrapper = new RelationshipWrapper(associationSet, sourceMember.Name, sourceKey, targetMember.Name, targetKey);
                                manager.AddNewRelation(wrapper, EntityState.Deleted); 
                            } 
                        }
                    } 
                }
            }
            else
            { 
                IRelatedEnd relatedEnd;
                if(sourceEntity.RelationshipManager.TryGetRelatedEnd(sourceMember.DeclaringType.FullName, targetMember.Name, out relatedEnd)) 
                { 
                    SetIsLoadedForSpan((RelatedEnd)relatedEnd, false);
                } 
            }

            // else there is nothing else for us to do, the relationship has been handled already
            return entity; 
        }
 
        ///  
        /// Sets the IsLoaded flag to "true"
        /// There are also rules for when this can be set based on MergeOption and the current value(s) in the related end. 
        /// 
        private void SetIsLoadedForSpan(RelatedEnd relatedEnd, bool forceToTrue)
        {
            Debug.Assert(relatedEnd != null, "RelatedEnd should not be null"); 

            // We can now say this related end is "Loaded" 
            // The cases where we should set this to true are: 
            // AppendOnly: the related end is empty and does not point to a stub
            // PreserveChanges: the related end is empty and does not point to a stub (otherwise, an Added item exists and IsLoaded should not change) 
            // OverwriteChanges: always
            // NoTracking: always
            if (!forceToTrue)
            { 
                // Detect the empty value state of the relatedEnd
                forceToTrue = relatedEnd.IsEmpty(); 
                EntityReference reference = relatedEnd as EntityReference; 
                if (reference != null)
                { 
                    forceToTrue &= reference.EntityKey == null;
                }
            }
            if (forceToTrue || this.MergeOption == MergeOption.OverwriteChanges) 
            {
                relatedEnd.SetIsLoaded(true); 
            } 
        }
 
        /// 
        /// REQUIRES:: entity is not null; entity set may be null.
        /// Sets up RelationshipManager on IEntityWithRelationships instance. Returns the input
        /// entity so that the call can be composed within a ShaperEmitter Expression delegate. 
        /// 
        public TEntity HandleIEntityWithRelationships(TEntity entity, EntitySet entitySet) 
            where TEntity : IEntityWithRelationships 
        {
            Debug.Assert(null != entity, "entity null"); 
            if (entitySet != null)
            {
                EntityUtil.AttachContext(entity, this.Context, entitySet, this.MergeOption == MergeOption.NoTracking ? MergeOption.NoTracking : MergeOption.AppendOnly);
            } 
            return entity;
        } 
 
        /// 
        /// REQUIRES:: entity is not null and MergeOption is OverwriteChanges or PreserveChanges 
        /// Calls through to HandleEntity after retrieving the EntityKey from the given entity.
        /// 
        public TEntity HandleIEntityWithKey(TEntity entity, EntitySet entitySet)
            where TEntity : IEntityWithKey 
        {
            return HandleEntity(entity, entity.EntityKey, entitySet); 
        } 

        ///  
        /// Calls through to the specified RecordState to set the value for the specified column ordinal.
        /// 
        public bool SetColumnValue(int recordStateSlotNumber, int ordinal, object value)
        { 
            RecordState recordState = (RecordState)this.State[recordStateSlotNumber];
            recordState.SetColumnValue(ordinal, value); 
            return true;  // TRICKY: return true so we can use BitwiseOr expressions to string these guys together. 
        }
 
        /// 
        /// Calls through to the specified RecordState to set the value for the EntityRecordInfo.
        /// 
        public bool SetEntityRecordInfo(int recordStateSlotNumber, EntityKey entityKey, EntitySet entitySet) 
        {
            RecordState recordState = (RecordState)this.State[recordStateSlotNumber]; 
            recordState.SetEntityRecordInfo(entityKey, entitySet); 
            return true;  // TRICKY: return true so we can use BitwiseOr expressions to string these guys together.
        } 

        /// 
        /// REQUIRES:: should be called only by delegate allocating this state.
        /// Utility method assigning a value to a state slot. Returns an arbitrary value 
        /// allowing the method call to be composed in a ShapeEmitter Expression delegate.
        ///  
        public bool SetState(int ordinal, T value) 
        {
            this.State[ordinal] = value; 
            return true;  // TRICKY: return true so we can use BitwiseOr expressions to string these guys together.
        }

        ///  
        /// REQUIRES:: should be called only by delegate allocating this state.
        /// Utility method assigning a value to a state slot and return the value, allowing 
        /// the value to be accessed/set in a ShapeEmitter Expression delegate and later 
        /// retrieved.
        ///  
        public T SetStatePassthrough(int ordinal, T value)
        {
            this.State[ordinal] = value;
            return value; 
        }
 
        ///  
        /// Used to retrieve a property value with exception handling. Normally compiled
        /// delegates directly call typed methods on the DbDataReader (e.g. GetInt32) 
        /// but when an exception occurs we retry using this method to potentially get
        /// a more useful error message to the user.
        /// 
        public TProperty GetPropertyValueWithErrorHandling(int ordinal, string propertyName, string typeName) 
        {
            TProperty result = new PropertyErrorHandlingValueReader(propertyName, typeName).GetValue(this.Reader, ordinal); 
            return result; 
        }
 
        /// 
        /// Used to retrieve a column value with exception handling. Normally compiled
        /// delegates directly call typed methods on the DbDataReader (e.g. GetInt32)
        /// but when an exception occurs we retry using this method to potentially get 
        /// a more useful error message to the user.
        ///  
        public TColumn GetColumnValueWithErrorHandling(int ordinal) 
        {
            TColumn result = new ColumnErrorHandlingValueReader().GetValue(this.Reader, ordinal); 
            return result;
        }

        #endregion 

        #region helper methods (used by runtime callable code) 
 
        private void CheckClearedEntryOnSpan(object targetValue, IEntityWithRelationships sourceEntity, EntityKey sourceKey, AssociationEndMember targetMember)
        { 
            // If a relationship does not exist on the server but does exist on the client,
            // we may need to remove it, depending on the current state and the MergeOption
            if ((null != (object)sourceKey) && (null == targetValue) &&
                (this.MergeOption == MergeOption.PreserveChanges || 
                 this.MergeOption == MergeOption.OverwriteChanges))
            { 
                // When the spanned value is null, it may be because the spanned association applies to a 
                // subtype of the entity's type, and the entity is not actually an instance of that type.
                AssociationEndMember sourceEnd = MetadataHelper.GetOtherAssociationEnd(targetMember); 
                EdmType expectedSourceType = ((RefType)sourceEnd.TypeUsage.EdmType).ElementType;
                TypeUsage entityTypeUsage;
                if (!this.Context.Perspective.TryGetType(sourceEntity.GetType(), out entityTypeUsage) ||
                    entityTypeUsage.EdmType.EdmEquals(expectedSourceType) || 
                    TypeSemantics.IsSubTypeOf(entityTypeUsage.EdmType, expectedSourceType))
                { 
                    // Otherwise, the source entity is the correct type (exactly or a subtype) for the source 
                    // end of the spanned association, so validate that the relationhip that was spanned is
                    // part of the Container owning the EntitySet of the root entity. 
                    // This can be done by comparing the EntitySet  of the row's entity to the relationships
                    // in the Container and their AssociationSetEnd's type
                    CheckClearedEntryOnSpan(sourceKey, sourceEntity, targetMember);
                } 
            }
        } 
 
        private void CheckClearedEntryOnSpan(EntityKey sourceKey, IEntityWithRelationships sourceEntity, AssociationEndMember targetMember)
        { 
            Debug.Assert(null != (object)sourceKey);
            Debug.Assert(sourceEntity != null);
            Debug.Assert(targetMember != null);
            Debug.Assert(this.Context != null); 

            AssociationEndMember sourceMember = MetadataHelper.GetOtherAssociationEnd(targetMember); 
 
            EntityContainer entityContainer = this.Context.MetadataWorkspace.GetEntityContainer(sourceKey.EntityContainerName,
                DataSpace.CSpace); 
            EntitySet sourceEntitySet;
            AssociationSet associationSet = MetadataHelper.GetAssociationsForEntitySetAndAssociationType(entityContainer, sourceKey.EntitySetName,
                (AssociationType)sourceMember.DeclaringType, sourceMember.Name, out sourceEntitySet);
 
            if (associationSet != null)
            { 
                Debug.Assert(associationSet.AssociationSetEnds[sourceMember.Name].EntitySet == sourceEntitySet); 
                ObjectStateManager.RemoveRelationships(Context, MergeOption, associationSet, sourceKey, sourceMember);
 

            }
        }
 
        /// 
        /// Wire's one or more full-spanned entities into the state manager; used by 
        /// both full-spanned collections and full-spanned entities. 
        /// 
        private void FullSpanAction(IEntityWithRelationships sourceEntity, IList spannedEntities, AssociationEndMember targetMember) 
        {
            EntityUtil.CheckKeyForRelationship(sourceEntity, MergeOption);
            EntityKey sourceKey = ObjectStateManager.FindKeyOnEntityWithRelationships(sourceEntity);
 
            if (sourceEntity != null)
            { 
                IRelatedEnd relatedEnd; 
                if(sourceEntity.RelationshipManager.TryGetRelatedEnd(targetMember.DeclaringType.FullName, targetMember.Name, out relatedEnd))
                { 
                    // Add members of the list to the source entity (item in column 0)
                    AssociationEndMember sourceMember = MetadataHelper.GetOtherAssociationEnd(targetMember);

                    int count = ObjectStateManager.UpdateRelationships(this.Context, this.MergeOption, (AssociationSet)relatedEnd.RelationshipSet, sourceMember, sourceKey, sourceEntity, targetMember, (List)spannedEntities, true); 

                    SetIsLoadedForSpan((RelatedEnd)relatedEnd, count > 0); 
                } 
            }
        } 

        #region update existing ObjectStateEntry

        private void UpdateEntry(TEntity entity, ObjectStateEntry existingEntry) 
        {
            Debug.Assert(null != entity, "null entity"); 
            Debug.Assert(null != existingEntry, "null ObjectStateEntry"); 
            Debug.Assert(null != existingEntry.Entity, "ObjectStateEntry without Entity");
 
            Type clrType = typeof(TEntity);
            if (clrType != existingEntry.Entity.GetType())
            {
                throw EntityUtil.RecyclingEntity(existingEntry.EntityKey, clrType, existingEntry.Entity.GetType()); 
            }
 
            if (EntityState.Added == existingEntry.State) 
            {
                throw EntityUtil.AddedEntityAlreadyExists(existingEntry.EntityKey); 
            }

            if (MergeOption.AppendOnly != MergeOption)
            {   // existing entity, update CSpace values in place 
                Debug.Assert(EntityState.Added != existingEntry.State, "entry in State=Added");
                Debug.Assert(EntityState.Detached != existingEntry.State, "entry in State=Detached"); 
 
                if (MergeOption.OverwriteChanges == MergeOption)
                { 
                    if (EntityState.Deleted == existingEntry.State)
                    {
                        existingEntry.RevertDelete();
                    } 
                    Shaper.UpdateRecord(entity, existingEntry.CurrentValues);
                    existingEntry.AcceptChanges(); 
                } 
                else
                { 
                    Debug.Assert(MergeOption.PreserveChanges == MergeOption, "not MergeOption.PreserveChanges");
                    if (EntityState.Unchanged == existingEntry.State)
                    {
                        // same behavior as MergeOption.OverwriteChanges 
                        UpdateRecord(entity, existingEntry.CurrentValues);
                        existingEntry.AcceptChanges(); 
                    } 
                    else
                    { 
                        Shaper.UpdateRecord(entity, existingEntry.EditableOriginalValues);
                    }
                }
            } 
        }
 
        static internal void UpdateRecord(object value, CurrentValueRecord current) 
        {
            Debug.Assert(null != value, "null value"); 
            Debug.Assert(null != current, "null CurrentValueRecord");

            // get Metadata for type
            StateManagerTypeMetadata typeMetadata = current._metadata; 
            DataRecordInfo recordInfo = typeMetadata.DataRecordInfo;
            IBaseList structure = TypeHelpers.GetAllStructuralMembers(recordInfo.RecordType); 
            foreach (FieldMetadata field in recordInfo.FieldMetadata) 
            {
                int index = structure.IndexOf(field.FieldType); 
                object fieldValue = typeMetadata.Member(index).GetValue(value) ?? DBNull.Value;

                if (Helper.IsComplexType(field.FieldType.TypeUsage.EdmType))
                { 
                    object existing = current.GetValue(index);
                    // Ensure that the existing ComplexType value is not null. This is not supported. 
                    if (existing == DBNull.Value) 
                    {
                        throw EntityUtil.NullableComplexTypesNotSupported(field.FieldType.Name); 
                    }
                    else if (fieldValue != DBNull.Value)
                    {
                        // There is both an IExtendedDataRecord and an existing CurrentValueRecord 
                        Shaper.UpdateRecord(fieldValue, (CurrentValueRecord)existing);
                    } 
                } 
                else
                { 
                    Debug.Assert(Helper.IsPrimitiveType(field.FieldType.TypeUsage.EdmType),
                                 "Property is not PrimitiveType");

                    object existing = current.GetValue(index) ?? DBNull.Value; 
                    if ((existing != fieldValue) &&
                        (((object)DBNull.Value == fieldValue) || 
                         ((object)DBNull.Value == existing) || 
                         (!existing.Equals(fieldValue))))
                    { 
                        current.SetValue(index, fieldValue);
                    }
                }
            } 
        }
 
        #endregion 

        #endregion 

        #region nested types
        private abstract class ErrorHandlingValueReader
        { 
            /// 
            /// Gets value from reader using the same pattern as the materializer delegate. Avoids 
            /// the need to compile multiple delegates for error handling. If there is a failure 
            /// reading a value
            ///  
            internal T GetValue(DbDataReader reader, int ordinal)
            {
                T result;
                bool isNullable; 
                MethodInfo readerMethod = Translator.GetReaderMethod(typeof(T), out isNullable);
                if (reader.IsDBNull(ordinal)) 
                { 
                    try
                    { 
                        result = (T)(object)null;
                    }
                    catch (NullReferenceException)
                    { 
                        // NullReferenceException is thrown when casting null to a value type.
                        // We don't use isNullable here because of an issue with GetReaderMethod 
                        // 
                        throw CreateNullValueException();
                    } 
                }
                else
                {
                    try 
                    {
                        // use the specific reader.GetXXX method 
                        result = (T)readerMethod.Invoke(reader, new object[] { ordinal }); 
                    }
                    catch (Exception e) 
                    {
                        if (EntityUtil.IsCatchableExceptionType(e))
                        {
                            // determine if the problem is with the result type 
                            // (note that if we throw on this call, it's ok
                            // for it to percolate up -- we only intercept type 
                            // and null mismatches) 
                            object untypedResult = reader.GetValue(ordinal);
                            Type resultType = null == untypedResult ? null : untypedResult.GetType(); 
                            if (!typeof(T).IsAssignableFrom(resultType))
                            {
                                throw CreateWrongTypeException(resultType);
                            } 
                        }
                        throw; 
                    } 
                }
                return result; 
            }

            /// 
            /// Creates the exception thrown when the reader returns a null value 
            /// for a non nullable property/column.
            ///  
            protected abstract Exception CreateNullValueException(); 

            ///  
            /// Creates the exception thrown when the reader returns a value with
            /// an incompatible type.
            /// 
            protected abstract Exception CreateWrongTypeException(Type resultType); 
        }
 
        private class ColumnErrorHandlingValueReader : ErrorHandlingValueReader 
        {
            internal ColumnErrorHandlingValueReader() 
            {
            }

            protected override Exception CreateNullValueException() 
            {
                return EntityUtil.ValueNullReferenceCast(typeof(TColumn)); 
            } 

            protected override Exception  CreateWrongTypeException(Type resultType) 
            {
                return EntityUtil.ValueInvalidCast(resultType, typeof(TColumn));
            }
        } 

        private class PropertyErrorHandlingValueReader : ErrorHandlingValueReader 
        { 
            private readonly string _propertyName;
            private readonly string _typeName; 

            internal PropertyErrorHandlingValueReader(string propertyName, string typeName)
            {
                _propertyName = propertyName; 
                _typeName = typeName;
            } 
 
            protected override Exception CreateNullValueException()
            { 
                return EntityUtil.Constraint(
                                        System.Data.Entity.Strings.Materializer_SetInvalidValue(
                                        (Nullable.GetUnderlyingType(typeof(TProperty)) ?? typeof(TProperty)).Name,
                                        _typeName, _propertyName, "null")); 
            }
 
            protected override Exception CreateWrongTypeException(Type resultType) 
            {
                return EntityUtil.InvalidOperation( 
                                        System.Data.Entity.Strings.Materializer_SetInvalidValue(
                                        (Nullable.GetUnderlyingType(typeof(TProperty)) ?? typeof(TProperty)).Name,
                                        _typeName, _propertyName, resultType.Name));
            } 
        }
        #endregion 
    } 

    ///  
    /// Typed Shaper. Includes logic to enumerate results and wraps the _rootCoordinator,
    /// which includes materializer delegates for the root query collection.
    /// 
    internal sealed class Shaper : Shaper 
    {
        #region private state 
 
        /// 
        /// Shapers and Coordinators work together in harmony to materialize the data 
        /// from the store; the shaper contains the state, the coordinator contains the
        /// code.
        /// 
        internal readonly Coordinator RootCoordinator; 

        ///  
        /// What we need to call when we read to ensure we're maintaining security; will 
        /// do the approprate security demands.
        ///  
        private readonly Action CheckPermissionsAction;

        /// 
        /// Which type of query is this, object layer (true) or value layer (false) 
        /// 
        private readonly bool IsObjectQuery; 
 
        /// 
        /// Keeps track of whether we've completed processing or not. 
        /// 
        private bool _isActive;

        ///  
        /// The enumerator we're using to read data; really only populated for value
        /// layer queries. 
        ///  
        private IEnumerator _rootEnumerator;
 
        /// 
        /// Whether the current value of _rootEnumerator has been returned by a bridge
        /// data reader.
        ///  
        private bool _dataWaiting;
 
        #endregion 

        #region constructor 

        internal Shaper(DbDataReader reader, ObjectContext context, MetadataWorkspace workspace, MergeOption mergeOption, int stateCount, CoordinatorFactory rootCoordinatorFactory, Action checkPermissions)
            : base(reader, context, workspace, mergeOption, stateCount)
        { 
            RootCoordinator = new Coordinator(rootCoordinatorFactory, /*parent*/ null, /*next*/ null);
            CheckPermissionsAction = checkPermissions; 
            IsObjectQuery = !(typeof(T) == typeof(RecordState)); 
            _isActive = true;
            RootCoordinator.Initialize(this); 
        }

        #endregion
 
        #region "public" surface area
 
        ///  
        /// Events raised when the shaper has finished enumerating results. Useful for callback
        /// to set parameter values. 
        /// 
        internal event EventHandler OnDone;

        ///  
        /// Used to handle the read-ahead requirements of value-layer queries.  This
        /// field indicates the status of the current value of the _rootEnumerator; when 
        /// a bridge data reader "accepts responsibility" for the current value, it sets 
        /// this to false.
        ///  
        internal bool DataWaiting
        {
            get { return _dataWaiting; }
            set { _dataWaiting = value; } 
        }
 
        ///  
        /// The enumerator that the value-layer bridge will use to read data; all nested
        /// data readers need to use the same enumerator, so we put it on the Shaper, since 
        /// that is something that all the nested data readers (and data records) have access
        /// to -- it prevents us from having to pass two objects around.
        /// 
        internal IEnumerator RootEnumerator 
        {
            get 
            { 
                if (_rootEnumerator == null)
                { 
                    InitializeRecordStates(RootCoordinator.CoordinatorFactory);
                    _rootEnumerator = GetEnumerator();
                }
                return _rootEnumerator; 
            }
        } 
 
        /// 
        /// Initialize the RecordStateFactory objects in their StateSlots. 
        /// 
        private void InitializeRecordStates(CoordinatorFactory coordinatorFactory)
        {
            foreach (RecordStateFactory recordStateFactory in coordinatorFactory.RecordStateFactories) 
            {
                State[recordStateFactory.StateSlotNumber] = recordStateFactory.Create(coordinatorFactory); 
            } 

            foreach (CoordinatorFactory nestedCoordinatorFactory in coordinatorFactory.NestedCoordinators) 
            {
                InitializeRecordStates(nestedCoordinatorFactory);
            }
        } 

        public IEnumerator GetEnumerator() 
        { 
            // we can use a simple enumerator if there are no nested results, no keys and no "has data"
            // discriminator 
            if (RootCoordinator.CoordinatorFactory.IsSimple)
            {
                return new SimpleEnumerator(this);
            } 
            else
            { 
                RowNestedResultEnumerator rowEnumerator = new Shaper.RowNestedResultEnumerator(this); 

                if (this.IsObjectQuery) 
                {
                    return new ObjectQueryNestedEnumerator(rowEnumerator);
                }
                else 
                {
                    return (IEnumerator)(object)(new RecordStateEnumerator(rowEnumerator)); 
                } 
            }
        } 

        #endregion

        #region enumerator helpers 

        ///  
        /// Called when enumeration of results has completed. 
        /// 
        private void Finally() 
        {
            if (_isActive)
            {
                _isActive = false; 

                // I'd prefer not to special case this, but value-layer behavior is that you 
                // must explicitly close the data reader; if we automatically dispose of the 
                // reader here, we won't have that behavior.
                if (IsObjectQuery) 
                {
                    this.Reader.Dispose();
                }
 
                // This case includes when the ObjectResult is disposed before it
                // created an ObjectQueryEnumeration; at this time, the connection can be released 
                if (this.Context != null) 
                {
                    this.Context.ReleaseConnection(); 
                }

                if (null != this.OnDone)
                { 
                    this.OnDone(this, new EventArgs());
                } 
            } 
        }
 
        /// 
        /// Reads the next row from the store. If there is a failure, throws an exception message
        /// in some scenarios (note that we respond to failure rather than anticipate failure,
        /// avoiding repeated checks in the inner materialization loop) 
        /// 
        private bool StoreRead() 
        { 
            try
            { 
                return this.Reader.Read();
            }
            catch (Exception e)
            { 
                // check if the reader is closed; if so, throw friendlier exception
                if (this.Reader.IsClosed) 
                { 
                    const string operation = "Read";
                    throw EntityUtil.DataReaderClosed(operation); 
                }

                // wrap exception if necessary
                if (EntityUtil.IsCatchableEntityExceptionType(e)) 
                {
                    throw EntityUtil.CommandExecution(System.Data.Entity.Strings.EntityClient_StoreReaderFailed, e); 
                } 
                throw;
            } 
        }

        #endregion
 
        #region simple enumerator
 
        ///  
        /// Optimized enumerator for queries not including nested results.
        ///  
        private class SimpleEnumerator : IEnumerator
        {
            private readonly Shaper _shaper;
 
            internal SimpleEnumerator(Shaper shaper)
            { 
                _shaper = shaper; 
            }
 
            public T Current
            {
                get { return _shaper.RootCoordinator.Current; }
            } 

            object System.Collections.IEnumerator.Current 
            { 
                get { return _shaper.RootCoordinator.Current; }
            } 

            public void Dispose()
            {
                // For backwards compatibility, we set the current value to the 
                // default value, so you can still call Current.
                _shaper.RootCoordinator.SetCurrentToDefault(); 
                _shaper.Finally(); 
            }
 
            public bool MoveNext()
            {
                if (!_shaper._isActive)
                { 
                    return false;
                } 
                if (_shaper.StoreRead()) 
                {
                    if (null != _shaper.CheckPermissionsAction) 
                    {
                        _shaper.CheckPermissionsAction();
                    }
                    _shaper.RootCoordinator.ReadNextElement(_shaper); 
                    return true;
                } 
                this.Dispose(); 
                return false;
            } 

            public void Reset()
            {
                throw EntityUtil.NotSupported(); 
            }
        } 
 
        #endregion
 
        #region nested enumerator

        /// 
        /// Enumerates (for each row in the input) an array of all coordinators producing new elements. The array 
        /// contains a position for each 'depth' in the result. A null value in any position indicates that no new
        /// results were produced for the given row at the given depth. It is possible for a row to contain no 
        /// results for any row. 
        /// 
        private class RowNestedResultEnumerator : IEnumerator 
        {
            private readonly Shaper _shaper;
            private readonly Coordinator[] _current;
 
            internal RowNestedResultEnumerator(Shaper shaper)
            { 
                _shaper = shaper; 
                _current = new Coordinator[_shaper.RootCoordinator.MaxDistanceToLeaf() + 1];
            } 

            public Coordinator[] Current
            {
                get { return _current; } 
            }
 
            public void Dispose() 
            {
                _shaper.Finally(); 
            }

            object System.Collections.IEnumerator.Current
            { 
                get { return _current; }
            } 
 
            public bool MoveNext()
            { 
                Coordinator currentCoordinator = _shaper.RootCoordinator;

                if (!_shaper.StoreRead())
                { 
                    // Reset all collections
                    this.RootCoordinator.ResetCollection(_shaper); 
                    return false; 
                }
 
                int depth = 0;
                bool haveInitializedChildren = false;
                for (; depth < _current.Length; depth++)
                { 
                    // find a coordinator at this depth that currently has data (if any)
                    while (currentCoordinator != null && !currentCoordinator.CoordinatorFactory.HasData(_shaper)) 
                    { 
                        currentCoordinator = currentCoordinator.Next;
                    } 
                    if (null == currentCoordinator)
                    {
                        break;
                    } 

                    // check if this row contains a new element for this coordinator 
                    if (currentCoordinator.HasNextElement(_shaper)) 
                    {
                        // if we have children and haven't initialized them yet, do so now 
                        if (!haveInitializedChildren && null != currentCoordinator.Child)
                        {
                            currentCoordinator.Child.ResetCollection(_shaper);
                        } 
                        haveInitializedChildren = true;
 
                        // read the next element 
                        currentCoordinator.ReadNextElement(_shaper);
 
                        // place the coordinator in the result array to indicate there is a new
                        // element at this depth
                        _current[depth] = currentCoordinator;
                    } 
                    else
                    { 
                        // clear out the coordinator in result array to indicate there is no new 
                        // element at this depth
                        _current[depth] = null; 
                    }

                    // move to child (in the next iteration we deal with depth + 1
                    currentCoordinator = currentCoordinator.Child; 
                }
 
                // clear out all positions below the depth we reached before we ran out of data 
                for (; depth < _current.Length; depth++)
                { 
                    _current[depth] = null;
                }

                return true; 
            }
 
            public void Reset() 
            {
                throw EntityUtil.NotSupported(); 
            }

            internal Coordinator RootCoordinator
            { 
                get { return _shaper.RootCoordinator; }
            } 
        } 

        ///  
        /// Wraps RowNestedResultEnumerator and yields results appropriate to an ObjectQuery instance. In particular,
        /// root level elements (T) are returned only after aggregating all child elements.
        /// 
        private class ObjectQueryNestedEnumerator : IEnumerator 
        {
            private readonly RowNestedResultEnumerator _rowEnumerator; 
            private T _previousElement; 
            private State _state;
 
            internal ObjectQueryNestedEnumerator(RowNestedResultEnumerator rowEnumerator)
            {
                _rowEnumerator = rowEnumerator;
                _previousElement = default(T); 
                _state = State.Start;
            } 
 
            public T Current { get { return _previousElement; } }
 
            public void Dispose()
            {
                _rowEnumerator.Dispose();
            } 

            object System.Collections.IEnumerator.Current { get { return this.Current; } } 
 
            public bool MoveNext()
            { 
                // See the documentation for enum State to understand the behaviors and requirements
                // for each state.
                switch (_state)
                { 
                    case State.Start:
                        { 
                            if (TryReadToNextElement()) 
                            {
                                // if there's an element in the reader... 
                                ReadElement();
                            }
                            else
                            { 
                                // no data at all...
                                _state = State.NoRows; 
                            } 
                        };
                        break; 
                    case State.Reading:
                        {
                            ReadElement();
                        }; 
                        break;
                    case State.NoRowsLastElementPending: 
                        { 
                            // nothing to do but move to the next state...
                            _state = State.NoRows; 
                        };
                        break;
                }
 
                bool result;
                if (_state == State.NoRows) 
                { 
                    _previousElement = default(T);
                    result = false; 
                }
                else
                {
                    result = true; 
                }
 
                return result; 
            }
 
            /// 
            /// Requires: the row is currently positioned at the start of an element.
            ///
            /// Reads all rows in the element and sets up state for the next element (if any). 
            /// 
            private void ReadElement() 
            { 
                // remember the element we're currently reading
                _previousElement = _rowEnumerator.RootCoordinator.Current; 

                // now we need to read to the next element (or the end of the
                // reader) so that we can return the first element
                if (TryReadToNextElement()) 
                {
                    // we're positioned at the start of the next element (which 
                    // corresponds to the 'reading' state) 
                    _state = State.Reading;
                } 
                else
                {
                    // we're positioned at the end of the reader
                    _state = State.NoRowsLastElementPending; 
                }
            } 
 
            /// 
            /// Reads rows until the start of a new element is found. If no element 
            /// is found before all rows are consumed, returns false.
            /// 
            private bool TryReadToNextElement()
            { 
                bool result = false;
                while (_rowEnumerator.MoveNext()) 
                { 
                    // if we hit a new element, return true
                    if (_rowEnumerator.Current[0] != null) 
                    {
                        result = true;
                        break;
                    } 
                }
                return result; 
            } 

            public void Reset() 
            {
                _rowEnumerator.Reset();
            }
 
            /// 
            /// Describes the state of this enumerator with respect to the _rowEnumerator 
            /// it wraps. 
            /// 
            private enum State 
            {
                /// 
                /// No rows have been read yet
                ///  
                Start,
 
                ///  
                /// Positioned at the start of a new root element. The previous element must
                /// be stored in _previousElement. We read ahead in this manner so that 
                /// the previous element is fully populated (all of its children loaded)
                /// before returning.
                /// 
                Reading, 

                ///  
                /// Positioned past the end of the rows. The last element in the enumeration 
                /// has not yet been returned to the user however, and is stored in _previousElement.
                ///  
                NoRowsLastElementPending,

                /// 
                /// Positioned past the end of the rows. The last element has been returned to 
                /// the user.
                ///  
                NoRows, 
            }
        } 

        /// 
        /// Wraps RowNestedResultEnumerator and yields results appropriate to an EntityReader instance. In particular,
        /// yields RecordState whenever a new element becomes available at any depth in the result hierarchy. 
        /// 
        private class RecordStateEnumerator : IEnumerator 
        { 
            private readonly RowNestedResultEnumerator _rowEnumerator;
            private RecordState _current; 

            /// 
            /// Gets depth of coordinator we're currently consuming. If _depth == -1, it means we haven't started
            /// to consume the next row yet. 
            /// 
            private int _depth; 
            private bool _readerConsumed; 

            internal RecordStateEnumerator(RowNestedResultEnumerator rowEnumerator) 
            {
                _rowEnumerator = rowEnumerator;
                _current = null;
                _depth = -1; 
                _readerConsumed = false;
            } 
 
            public RecordState Current
            { 
                get { return _current; }
            }

            public void Dispose() 
            {
                _rowEnumerator.Dispose(); 
            } 

            object System.Collections.IEnumerator.Current 
            {
                get { return _current; }
            }
 
            public bool MoveNext()
            { 
                if (!_readerConsumed) 
                {
                    while (true) 
                    {
                        // keep on cycling until we find a result
                        if (-1 == _depth || _rowEnumerator.Current.Length == _depth)
                        { 
                            // time to move to the next row...
                            if (!_rowEnumerator.MoveNext()) 
                            { 
                                // no more rows...
                                _current = null; 
                                _readerConsumed = true;
                                break;
                            }
 
                            _depth = 0;
                        } 
 
                        // check for results at the current depth
                        Coordinator currentCoordinator = _rowEnumerator.Current[_depth]; 
                        if (null != currentCoordinator)
                        {
                            _current = ((Coordinator)currentCoordinator).Current;
                            _depth++; 
                            break;
                        } 
 
                        _depth++;
                    } 
                }

                return !_readerConsumed;
            } 

            public void Reset() 
            { 
                _rowEnumerator.Reset();
            } 
        }

        #endregion
 
    }
} 

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