UpdateTranslator.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 / Map / Update / Internal / UpdateTranslator.cs / 4 / UpdateTranslator.cs

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

using System.Collections.Generic; 
using System.Data.Objects;
using System.Data.Common.Utils;
using System.Data.Common.CommandTrees;
using System.Data.Common; 
using System.Threading;
using System.Collections.ObjectModel; 
using System.Diagnostics; 
using System.Data.Metadata.Edm;
using System.Data.EntityClient; 
using System.Globalization;
using System.Data.Entity;
using System.Linq;
 
namespace System.Data.Mapping.Update.Internal
{ 
    ///  
    /// This class performs to following tasks to persist C-Space changes to the store:
    ///  
    /// Extract changes from the entity state manager
    /// Group changes by C-Space extent
    /// For each affected S-Space table, perform propagation (get changes in S-Space terms)
    /// Merge S-Space inserts and deletes into updates where appropriate 
    /// Produce S-Space commands implementating the modifications (insert, delete and update SQL statements)
    ///  
    ///  
    internal partial class UpdateTranslator
    { 
        #region Constructors
        /// 
        /// Constructs a grouper based on the contents of the given entity state manager.
        ///  
        /// Entity state manager containing changes to be processed.
        /// Metadata workspace. 
        /// Map connection 
        /// Timeout for update commands; null means 'use provider default'
        private UpdateTranslator(IEntityStateManager stateManager, MetadataWorkspace metadataWorkspace, EntityConnection connection, int? commandTimeout) 
        {
            EntityUtil.CheckArgumentNull(stateManager, "stateManager");
            EntityUtil.CheckArgumentNull(metadataWorkspace, "metadataWorkspace");
            EntityUtil.CheckArgumentNull(connection, "connection"); 

            // propagation state 
            m_changes = new Dictionary(); 
            m_functionChanges = new Dictionary>();
            m_stateEntries = new List(); 
            m_knownEntityKeys = new Set();
            m_requiredEntities = new Dictionary();
            m_optionalEntities = new Set();
            m_includedValueEntities = new Set(); 

            // workspace state 
            m_commandTreeContext = new DbQueryCommandTree(metadataWorkspace, DataSpace.CSpace); 
            m_viewLoader = metadataWorkspace.GetUpdateViewLoader();
            m_stateManager = stateManager; 

            // ancillary propagation services
            m_recordConverter = new RecordConverter(this);
            m_constraintValidator = new RelationshipConstraintValidator(this); 

            m_providerServices = DbProviderServices.GetProviderServices(connection.StoreProviderFactory); 
            m_connection = connection; 
            m_commandTimeout = commandTimeout;
 
            // metadata cache
            m_extractorMetadata = new Dictionary(EqualityComparer.Default);

            // key management 
            KeyManager = new KeyManager(this);
            KeyComparer = CompositeKey.CreateComparer(KeyManager); 
        } 

        #endregion 

        #region Fields
        // propagation state
        private readonly Dictionary m_changes; 
        private readonly Dictionary> m_functionChanges;
        private readonly List m_stateEntries; 
        private readonly Set m_knownEntityKeys; 
        private readonly Dictionary m_requiredEntities;
        private readonly Set m_optionalEntities; 
        private readonly Set m_includedValueEntities;

        // workspace state
        private readonly DbCommandTree m_commandTreeContext; 
        private readonly ViewLoader m_viewLoader;
        private readonly IEntityStateManager m_stateManager; 
 
        // ancillary propagation services
        private readonly RecordConverter m_recordConverter; 
        private readonly RelationshipConstraintValidator m_constraintValidator;

        // provider information
        private readonly DbProviderServices m_providerServices; 
        private readonly EntityConnection m_connection;
        private readonly int? m_commandTimeout; 
        private Dictionary m_functionCommandDefinitions; 

        // metadata cache 
        private readonly Dictionary m_extractorMetadata;

        // static members
        private static readonly List s_emptyMemberList = new List(); 
        #endregion
 
        #region Properties 
        /// 
        /// Gets workspace used in this session. 
        /// 
        internal MetadataWorkspace MetadataWorkspace
        {
            get { return m_commandTreeContext.MetadataWorkspace; } 
        }
 
        ///  
        /// Gets key manager that handles interpretation of keys (including resolution of
        /// referential-integrity/common value constraints) 
        /// 
        internal readonly KeyManager KeyManager;

        ///  
        /// Gets the view loader metadata wrapper for the current workspace.
        ///  
        internal ViewLoader ViewLoader 
        {
            get { return m_viewLoader; } 
        }

        /// 
        /// Gets record converter which translates state entry records into propagator results. 
        /// 
        internal RecordConverter RecordConverter 
        { 
            get { return m_recordConverter; }
        } 

        /// 
        /// Gets command timeout for update commands. If null, use default.
        ///  
        internal int? CommandTimeout
        { 
            get { return m_commandTimeout; } 
        }
 
        internal readonly IEqualityComparer KeyComparer;
        #endregion

        #region Methods 
        /// 
        /// Registers any referential constraints contained in the state entry (so that 
        /// constrained members have the same identifier values). Only processes relationships 
        /// with referential constraints defined.
        ///  
        /// State entry
        internal void RegisterReferentialConstraints(IEntityStateEntry stateEntry)
        {
            if (stateEntry.IsRelationship) 
            {
                AssociationSet associationSet = (AssociationSet)stateEntry.EntitySet; 
                if (0 < associationSet.ElementType.ReferentialConstraints.Count) 
                {
                    DbDataRecord record = stateEntry.State == EntityState.Added ? 
                        (DbDataRecord)stateEntry.CurrentValues : stateEntry.OriginalValues;
                    foreach (ReferentialConstraint constraint in associationSet.ElementType.ReferentialConstraints)
                    {
                        // retrieve keys at the ends 
                        EntityKey principalKey = (EntityKey)record[constraint.FromRole.Name];
                        EntityKey dependentKey = (EntityKey)record[constraint.ToRole.Name]; 
 
                        // associate keys, where the from side 'owns' the to side
                        using (ReadOnlyMetadataCollection.Enumerator principalPropertyEnum = constraint.FromProperties.GetEnumerator()) 
                        using (ReadOnlyMetadataCollection.Enumerator dependentPropertyEnum = constraint.ToProperties.GetEnumerator())
                        {
                            while (principalPropertyEnum.MoveNext() && dependentPropertyEnum.MoveNext())
                            { 
                                int principalKeyMemberCount;
                                int dependentKeyMemberCount; 
 
                                // get offsets for from and to key properties
                                int principalOffset = GetKeyMemberOffset(constraint.FromRole, principalPropertyEnum.Current, 
                                    out principalKeyMemberCount);
                                int dependentOffset = GetKeyMemberOffset(constraint.ToRole, dependentPropertyEnum.Current,
                                    out dependentKeyMemberCount);
 
                                long principalIdentifier = this.KeyManager.GetKeyIdentifierForMemberOffset(principalKey, principalOffset, principalKeyMemberCount);
                                long dependentIdentifier = this.KeyManager.GetKeyIdentifierForMemberOffset(dependentKey, dependentOffset, dependentKeyMemberCount); 
 
                                // register equivalence of identifiers
                                this.KeyManager.AddReferentialConstraint(stateEntry, dependentIdentifier, principalIdentifier); 
                            }
                        }
                    }
                } 
            }
        } 
 
        // requires: role must not be null and property must be a key member for the role end
        private static int GetKeyMemberOffset(RelationshipEndMember role, EdmProperty property, out int keyMemberCount) 
        {
            Debug.Assert(null != role);
            Debug.Assert(null != property);
            Debug.Assert(BuiltInTypeKind.RefType == role.TypeUsage.EdmType.BuiltInTypeKind, 
                "relationship ends must be of RefType");
            RefType endType = (RefType)role.TypeUsage.EdmType; 
            Debug.Assert(BuiltInTypeKind.EntityType == endType.ElementType.BuiltInTypeKind, 
                "relationship ends must reference EntityType");
            EntityType entityType = (EntityType)endType.ElementType; 
            keyMemberCount = entityType.KeyMembers.Count;
            return entityType.KeyMembers.IndexOf(property);
        }
 
        /// 
        /// Yields all relationship state entries with the given key as an end. 
        ///  
        /// 
        ///  
        internal IEnumerable GetRelationships(EntityKey entityKey)
        {
            return m_stateManager.FindRelationshipsByKey(entityKey);
        } 

        ///  
        /// Persists stateManager changes to the store. 
        /// 
        /// StateManager containing changes to persist. 
        /// Map adapter requesting the changes.
        /// Total number of state entries affected
        internal static Int32 Update(IEntityStateManager stateManager, IEntityAdapter adapter)
        { 
            IntPtr cookie;
            EntityBid.ScopeEnter(out cookie, ""); 
 
            // provider/connection details
            EntityConnection connection = (EntityConnection)adapter.Connection; 
            MetadataWorkspace metadataWorkspace = connection.GetMetadataWorkspace();
            int? commandTimeout = adapter.CommandTimeout;

            try 
            {
                UpdateTranslator translator = new UpdateTranslator(stateManager, metadataWorkspace, connection, commandTimeout); 
 
                // tracks values for identifiers in this session
                Dictionary identifierValues = new Dictionary(); 

                // tracks values for generated values in this session
                List> generatedValues = new List>();
 
                List orderedCommands = translator.ProduceCommands();
 
                // used to track the source of commands being processed in case an exception is thrown 
                UpdateCommand source = null;
                try 
                {
                    foreach (UpdateCommand command in orderedCommands)
                    {
                        // Remember the data sources so that we can throw meaningful exception 
                        source = command;
                        int rowsAffected = command.Execute(translator, connection, identifierValues, generatedValues); 
                        translator.ValidateRowsAffected(rowsAffected, source); 
                    }
                } 
                catch (Exception e)
                {
                    // we should not be wrapping all exceptions
                    if (UpdateTranslator.RequiresContext(e)) 
                    {
                        throw EntityUtil.Update(System.Data.Entity.Strings.Update_GeneralExecutionException, e, translator.DetermineStateEntriesFromSource(source)); 
                    } 
                    throw;
                } 

                translator.BackPropagateServerGen(generatedValues);

                int totalStateEntries = translator.AcceptChanges(adapter); 

                return totalStateEntries; 
            } 
            finally
            { 
                EntityBid.ScopeLeave(ref cookie);
            }
        }
 
        private List ProduceCommands()
        { 
            // load all modified state entries 
            PullModifiedEntriesFromStateManager();
            PullUnchangedEntriesFromStateManager(); 

            // check constraints
            m_constraintValidator.ValidateConstraints();
            this.KeyManager.ValidateReferentialIntegrityGraphAcyclic(); 

            // gather all commands (aggregate in a dependency orderer to determine operation order 
            IEnumerable dynamicCommands = this.ProduceDynamicCommands(); 
            IEnumerable functionCommands = this.ProduceFunctionCommands();
            UpdateCommandOrderer orderer = new UpdateCommandOrderer(dynamicCommands.Concat(functionCommands), this); 
            List orderedCommands = new List(orderer.Vertices.Count);
            foreach (UpdateCommand[] commands in orderer.TryStagedTopologicalSort())
            {
                // at each depth in the topological sort, apply sorting based on operation, 
                // table/entityset and keys to ensure consistent update ordering
                Array.Sort(commands); 
                orderedCommands.AddRange(commands); 
            }
 
            // if there is a remainder, it suggests a cycle.
            this.ValidateGraphPostSort(orderer);
            return orderedCommands;
        } 

        // effects: given rows affected, throws if the count suggests a concurrency failure. 
        // Throws a concurrency exception based on the current command sources (which allow 
        // us to populated the EntityStateEntries on UpdateException)
        private void ValidateRowsAffected(int rowsAffected, UpdateCommand source) 
        {
            // 0 rows affected indicates a concurrency failure; negative values suggest rowcount is off;
            // positive values suggest at least one row was affected (we generally expect exactly one,
            // but triggers/view logic/logging may change this value) 
            if (0 == rowsAffected)
            { 
                var stateEntries = DetermineStateEntriesFromSource(source); 
                throw EntityUtil.UpdateConcurrency(rowsAffected, null, stateEntries);
            } 
        }

        private IEnumerable DetermineStateEntriesFromSource(UpdateCommand source)
        { 
            if (null == source)
            { 
                return Enumerable.Empty(); 
            }
            return source.GetStateEntries(this); 
        }

        // effects: Given a list of pairs describing the contexts for server generated values and their actual
        // values, backpropagates to the relevant state entries 
        private void BackPropagateServerGen(List> generatedValues)
        { 
            foreach (KeyValuePair generatedValue in generatedValues) 
            {
                PropagatorResult context; 

                // check if a redirect to "owner" result is possible
                if (PropagatorResult.NullIdentifier == generatedValue.Key.Identifier ||
                    !KeyManager.TryGetIdentifierOwner(generatedValue.Key.Identifier, out context)) 
                {
                    // otherwise, just use the straightforward context 
                    context = generatedValue.Key; 
                }
 
                // check that the column is actually mapped to an entity property
                if (context.RecordOrdinal != PropagatorResult.NullOrdinal)
                {
                    object value = generatedValue.Value; 
                    CurrentValueRecord targetRecord = context.Record;
 
                    // determine if type compensation is required 
                    IExtendedDataRecord recordWithMetadata = (IExtendedDataRecord)targetRecord;
                    EdmMember member = recordWithMetadata.DataRecordInfo.FieldMetadata[context.RecordOrdinal].FieldType; 

                    value = value ?? DBNull.Value; // records expect DBNull rather than null
                    value = AlignReturnValue(value, member, context);
                    targetRecord.SetValue(context.RecordOrdinal, value); 
                }
            } 
        } 

        ///  
        /// Aligns a value returned from the store with the expected type for the member.
        /// 
        /// Value to convert.
        /// Metadata for the member being set. 
        /// The context generating the return value.
        /// Converted return value 
        private static object AlignReturnValue(object value, EdmMember member, PropagatorResult context) 
        {
            if (DBNull.Value.Equals(value)) 
            {
                // check if there is a nullability constraint on the value
                if (BuiltInTypeKind.EdmProperty == member.BuiltInTypeKind &&
                    !((EdmProperty)member).Nullable) 
                {
                    throw EntityUtil.Update(System.Data.Entity.Strings.Update_NullReturnValueForNonNullableMember( 
                        member.Name, 
                        member.DeclaringType.FullName), null);
                } 
            }
            else
            {
                // convert the value to the appropriate CLR type 
                Debug.Assert(BuiltInTypeKind.PrimitiveType == member.TypeUsage.EdmType.BuiltInTypeKind,
                    "we only allow return values that are instances of EDM primitive types"); 
                PrimitiveType primitiveType = (PrimitiveType)member.TypeUsage.EdmType; 
                Type clrType = primitiveType.ClrEquivalentType;
 
                try
                {
                    value = Convert.ChangeType(value, clrType, CultureInfo.InvariantCulture);
                } 
                catch (Exception e)
                { 
                    // we should not be wrapping all exceptions 
                    if (UpdateTranslator.RequiresContext(e))
                    { 
                        throw EntityUtil.Update(System.Data.Entity.Strings.Update_ReturnValueHasUnexpectedType(
                            value.GetType().FullName,
                            clrType.FullName,
                            member.Name, 
                            member.DeclaringType.FullName), e);
                    } 
                    throw; 
                }
            } 

            // return the adjusted value
            return value;
        } 

        ///  
        /// Accept changes to entities and relationships processed by this translator instance. 
        /// 
        /// Data adapter 
        /// Number of state entries affected.
        private int AcceptChanges(IEntityAdapter adapter)
        {
            int affectedCount = 0; 
            foreach (IEntityStateEntry stateEntry in m_stateEntries)
            { 
                // only count and accept changes for state entries that are being explicitly modified 
                if (EntityState.Unchanged != stateEntry.State)
                { 
                    if (adapter.AcceptChangesDuringUpdate)
                    {
                        stateEntry.AcceptChanges();
                    } 
                    affectedCount++;
                } 
            } 
            return affectedCount;
        } 

        /// 
        /// Gets extents for which this translator has identified changes to be handled
        /// by the standard update pipeline. 
        /// 
        /// Enumeration of modified C-Space extents. 
        private IEnumerable GetDynamicModifiedExtents() 
        {
            return m_changes.Keys; 
        }

        /// 
        /// Gets extents for which this translator has identified changes to be handled 
        /// by function mappings.
        ///  
        /// Enumreation of modified C-Space extents. 
        private IEnumerable GetFunctionModifiedExtents()
        { 
            return m_functionChanges.Keys;
        }

        ///  
        /// Produce dynamic store commands for this translator's changes.
        ///  
        /// Database commands in a safe order 
        private IEnumerable ProduceDynamicCommands()
        { 
            // Initialize DBCommand update compiler
            UpdateCompiler updateCompiler = new UpdateCompiler(this);

            // Determine affected 
            Set tables = new Set();
 
            foreach (EntitySetBase extent in GetDynamicModifiedExtents()) 
            {
                Set affectedTables = m_viewLoader.GetAffectedTables(extent); 
                //Since these extents don't have Functions defined for update operations,
                //the affected tables should be provided via MSL.
                //If we dont find any throw an exception
                if (affectedTables.Count == 0) 
                {
                    throw EntityUtil.Update(System.Data.Entity.Strings.Update_MappingNotFound( 
                        extent.Name), null /*stateEntries*/); 
                }
 
                foreach (EntitySet table in affectedTables)
                {
                    tables.Add(table);
                } 
            }
 
            // Determine changes to apply to each table 
            List changes = new List(tables.Count);
            foreach (EntitySet table in tables) 
            {
                DbQueryCommandTree umView = m_connection.GetMetadataWorkspace().GetCqtView(table);

                // Propagate changes to root of tree (at which point they are S-Space changes) 
                ChangeNode changeNode = Propagator.Propagate(this, umView);
 
                // Process changes for the table 
                TableChangeProcessor change = new TableChangeProcessor(table);
                foreach (UpdateCommand command in change.CompileCommands(changeNode, updateCompiler)) 
                {
                    yield return command;
                }
            } 
        }
 
        // Generates and caches a command definition for the given function 
        internal DbCommandDefinition GenerateCommandDefinition(StorageFunctionMapping functionMapping)
        { 
            if (null == m_functionCommandDefinitions)
            {
                m_functionCommandDefinitions = new Dictionary();
            } 
            DbCommandDefinition commandDefinition;
            if (!m_functionCommandDefinitions.TryGetValue(functionMapping, out commandDefinition)) 
            { 
                // synthesize a RowType for this mapping
                TypeUsage resultType = null; 
                if (null != functionMapping.ResultBindings && 0 < functionMapping.ResultBindings.Count)
                {
                    List properties = new List(functionMapping.ResultBindings.Count);
                    foreach (StorageFunctionResultBinding resultBinding in functionMapping.ResultBindings) 
                    {
                        properties.Add(new EdmProperty(resultBinding.ColumnName, resultBinding.Property.TypeUsage)); 
                    } 
                    RowType rowType = new RowType(properties);
                    CollectionType collectionType = new CollectionType(rowType); 
                    resultType = TypeUsage.Create(collectionType);
                }

                // construct DbFunctionCommandTree including implict return type 
                DbFunctionCommandTree tree = new DbFunctionCommandTree(m_commandTreeContext.MetadataWorkspace, DataSpace.SSpace,
                    functionMapping.Function, resultType); 
 
                // add function parameters
                foreach (FunctionParameter paramInfo in functionMapping.Function.Parameters) 
                {
                    tree.AddParameter(paramInfo.Name, paramInfo.TypeUsage);
                }
 
                commandDefinition = m_providerServices.CreateCommandDefinition(tree);
            } 
            return commandDefinition; 
        }
 
        // Produces all function commands in a safe order
        private IEnumerable ProduceFunctionCommands()
        {
            foreach (EntitySetBase extent in GetFunctionModifiedExtents()) 
            {
                // Get a handle on the appropriate translator 
                FunctionMappingTranslator translator = m_viewLoader.GetFunctionMappingTranslator(extent); 

                if (null != translator) 
                {
                    // Compile commands
                    foreach (ExtractedStateEntry stateEntry in GetExtentFunctionModifications(extent))
                    { 
                        FunctionUpdateCommand command = translator.Translate(this, stateEntry);
                        if (null != command) 
                        { 
                            yield return command;
                        } 
                    }
                }
            }
        } 

        ///  
        /// Gets a metadata wrapper for the given type. The wrapper makes 
        /// certain tasks in the update pipeline more efficient.
        ///  
        /// Structural type
        /// Metadata wrapper
        internal ExtractorMetadata GetExtractorMetadata(StructuralType type)
        { 
            ExtractorMetadata metadata;
            if (!m_extractorMetadata.TryGetValue(type, out metadata)) 
            { 
                metadata = new ExtractorMetadata(type, this);
                m_extractorMetadata.Add(type, metadata); 
            }
            return metadata;
        }
 
        /// 
        /// Validates dependency graph after sort has been performed to ensure there were no cycles 
        ///  
        /// Dependency orderer graph to validate
        private void ValidateGraphPostSort(UpdateCommandOrderer orderer) 
        {
            Debug.Assert(null != orderer, "Caller must verify parameters are not null");

            if (0 != orderer.Remainder.Count) 
            {
                Set stateEntries = new Set(); 
 
                foreach (UpdateCommand command in orderer.Remainder)
                { 
                    stateEntries.AddRange(command.GetStateEntries(this));
                }

                // throw exception containing all related state entries 
                throw EntityUtil.Update(System.Data.Entity.Strings.Update_ConstraintCycle, null, stateEntries);
            } 
        } 

        ///  
        /// Creates a command in the current context.
        /// 
        /// DbCommand tree
        /// DbCommand produced by the current provider. 
        internal DbCommand CreateCommand(DbModificationCommandTree commandTree)
        { 
            DbCommand command; 
            Debug.Assert(null != m_providerServices, "constructor ensures either the command definition " +
                    "builder or provider service is available"); 
            Debug.Assert(null != m_connection.StoreConnection, "EntityAdapter.Update ensures the store connection is set");
            try
            {
                command = m_providerServices.CreateCommand(commandTree); 
            }
            catch (Exception e) 
            { 
                // we should not be wrapping all exceptions
                if (UpdateTranslator.RequiresContext(e)) 
                {
                    // we don't wan't folks to have to know all the various types of exceptions that can
                    // occur, so we just rethrow a CommandDefinitionException and make whatever we caught
                    // the inner exception of it. 
                    throw EntityUtil.CommandCompilation(System.Data.Entity.Strings.EntityClient_CommandDefinitionPreparationFailed, e);
                } 
                throw; 
            }
            return command; 
        }


        ///  
        /// Determines whether the given exception requires additional context from the update pipeline (in other
        /// words, whether the exception should be wrapped in an UpdateException). 
        ///  
        /// Exception to test.
        /// true if exception should be wrapped; false otherwise 
        internal static bool RequiresContext(Exception e)
        {
            // if the exception isn't catchable, never wrap
            if (!EntityUtil.IsCatchableExceptionType(e)) { return false; } 

            // update and incompatible provider exceptions already contain the necessary context 
            return !(e is UpdateException) && !(e is ProviderIncompatibleException); 
        }
 
        #region Private initialization methods
        /// 
        /// Retrieve all modified entries from the state manager.
        ///  
        private void PullModifiedEntriesFromStateManager()
        { 
            // do a first pass over entries to register referential integrity constraints 
            // for server-generation (only relationship are interesting, and relationships can only
            // be added or deleted) 
            foreach (IEntityStateEntry entry in m_stateManager.GetEntityStateEntries(EntityState.Added | EntityState.Deleted))
            {
                RegisterReferentialConstraints(entry);
            } 

            foreach (IEntityStateEntry modifiedEntry in m_stateManager.GetEntityStateEntries(EntityState.Modified | EntityState.Added | EntityState.Deleted)) 
            { 
                LoadStateEntry(modifiedEntry);
            } 
        }


        ///  
        /// Retrieve all required/optional/value entries into the state manager. These are entries that --
        /// although unmodified -- affect or are affected by updates. 
        ///  
        private void PullUnchangedEntriesFromStateManager()
        { 
            foreach (KeyValuePair required in m_requiredEntities)
            {
                EntityKey key = required.Key;
 
                if (!m_knownEntityKeys.Contains(key))
                { 
                    // pull the value into the translator if we don't already it 
                    IEntityStateEntry requiredEntry;
 
                    if (m_stateManager.TryGetEntityStateEntry(key, out requiredEntry))
                    {
                        // load the object as a no-op update
                        LoadStateEntry(requiredEntry); 
                    }
                    else 
                    { 
                        // throw an exception
                        throw EntityUtil.UpdateMissingEntity(required.Value.Name, TypeHelpers.GetFullName(key.EntityContainerName, key.EntitySetName)); 
                    }
                }
            }
 
            foreach (EntityKey key in m_optionalEntities)
            { 
                if (!m_knownEntityKeys.Contains(key)) 
                {
                    IEntityStateEntry optionalEntry; 

                    if (m_stateManager.TryGetEntityStateEntry(key, out optionalEntry))
                    {
                        // load the object as a no-op update 
                        LoadStateEntry(optionalEntry);
                    } 
                } 
            }
 
            foreach (EntityKey key in m_includedValueEntities)
            {
                if (!m_knownEntityKeys.Contains(key))
                { 
                    IEntityStateEntry valueEntry;
 
                    if (m_stateManager.TryGetEntityStateEntry(key, out valueEntry)) 
                    {
                        // Convert state entry so that its values are known to the update pipeline. 
                        var result = m_recordConverter.ConvertCurrentValuesToPropagatorResult(valueEntry, null);
                    }
                }
            } 
        }
 
        ///  
        /// Validates and tracks a state entry being processed by this translator.
        ///  
        /// 
        private void ValidateAndRegisterStateEntry(IEntityStateEntry stateEntry)
        {
            EntityUtil.CheckArgumentNull(stateEntry, "stateEntry"); 

            EntitySetBase extent = stateEntry.EntitySet; 
            if (null == extent) 
            {
                throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.InvalidStateEntry, 1); 
            }

            // Determine the key. May be null if the state entry does not represent an entity.
            EntityKey entityKey = stateEntry.EntityKey; 
            IExtendedDataRecord record = null;
 
            // verify the structure of the entry values 
            if (0 != ((EntityState.Added | EntityState.Modified | EntityState.Unchanged) & stateEntry.State))
            { 
                // added, modified and unchanged entries have current values
                record = (IExtendedDataRecord)stateEntry.CurrentValues;
                ValidateRecord(extent, record, stateEntry);
            } 
            if (0 != ((EntityState.Modified | EntityState.Deleted | EntityState.Unchanged) & stateEntry.State))
            { 
                // deleted, modified and unchanged entries have original values 
                record = (IExtendedDataRecord)stateEntry.OriginalValues;
                ValidateRecord(extent, record, stateEntry); 
            }
            Debug.Assert(null != record, "every state entry must contain a record");

            // make sure the view loader has loaded all information about this extent 
            m_viewLoader.SyncInitializeEntitySet(extent, this.MetadataWorkspace);
 
            // check for required ends of relationships 
            AssociationSet associationSet = extent as AssociationSet;
            if (null != associationSet) 
            {
                AssociationSetMetadata associationSetMetadata = m_viewLoader.GetAssociationSetMetadata(associationSet);

                if (associationSetMetadata.HasEnds) 
                {
                    foreach (FieldMetadata field in record.DataRecordInfo.FieldMetadata) 
                    { 
                        // ends of relationship record must be EntityKeys
                        EntityKey end = (EntityKey)record.GetValue(field.Ordinal); 

                        // ends of relationships must have AssociationEndMember metadata
                        AssociationEndMember endMetadata = (AssociationEndMember)field.FieldType;
 
                        if (associationSetMetadata.RequiredEnds.Contains(endMetadata))
                        { 
                            if (!m_requiredEntities.ContainsKey(end)) 
                            {
                                m_requiredEntities.Add(end, associationSet); 
                            }
                        }

                        else if (associationSetMetadata.OptionalEnds.Contains(endMetadata)) 
                        {
                            AddValidAncillaryKey(end, m_optionalEntities); 
                        } 

                        else if (associationSetMetadata.IncludedValueEnds.Contains(endMetadata)) 
                        {
                            AddValidAncillaryKey(end, m_includedValueEntities);
                        }
                    } 
                }
 
                // register relationship with validator 
                m_constraintValidator.RegisterAssociation(associationSet, record, stateEntry);
            } 
            else
            {
                // register entity with validator
                m_constraintValidator.RegisterEntity(stateEntry); 
            }
 
            // add to the list of entries being tracked 
            m_stateEntries.Add(stateEntry);
            if (null != (object)entityKey) { m_knownEntityKeys.Add(entityKey); } 
        }

        /// 
        /// effects: given an entity key and a set, adds key to the set iff. the corresponding entity 
        /// is:
        /// 
        ///     not a stub (or 'key') entry, and; 
        ///     not a core element in the update pipeline (it's not being directly modified)
        ///  
        private void AddValidAncillaryKey(EntityKey key, Set keySet)
        {
            // Note: an entity is ancillary iff. it is unchanged (otherwise it is tracked as a "standard" changed entity)
            IEntityStateEntry endEntry; 
            if (m_stateManager.TryGetEntityStateEntry(key, out endEntry) && // make sure the entity is tracked
                !endEntry.IsKeyEntry && // make sure the entity is not a stub 
                endEntry.State == EntityState.Unchanged) // if the entity is being modified, it's already included anyways 
            {
                keySet.Add(key); 
            }
        }

        private void ValidateRecord(EntitySetBase extent, IExtendedDataRecord record, IEntityStateEntry entry) 
        {
            Debug.Assert(null != extent, "must be verified by caller"); 
 
            DataRecordInfo recordInfo;
            if ((null == record) || 
                (null == (recordInfo = record.DataRecordInfo)) ||
                (null == recordInfo.RecordType))
            {
                throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.InvalidStateEntry, 2); 
            }
 
            VerifyExtent(MetadataWorkspace, extent); 

            // additional validation happens lazily as values are loaded from the record 
        }

        // Verifies the given extent is present in the given workspace.
        private static void VerifyExtent(MetadataWorkspace workspace, EntitySetBase extent) 
        {
            // get the container to which the given extent belongs 
            EntityContainer actualContainer = extent.EntityContainer; 

            // try to retrieve the container in the given workspace 
            EntityContainer referenceContainer = null;
            if (null != actualContainer)
            {
                workspace.TryGetEntityContainer( 
                    actualContainer.Name, actualContainer.DataSpace, out referenceContainer);
            } 
 
            // determine if the given extent lives in a container from the given workspace
            // (the item collections for each container are reference equivalent when they are declared in the 
            // same item collection)
            if (null == actualContainer || null == referenceContainer ||
                !Object.ReferenceEquals(actualContainer, referenceContainer))
            { 
                //
 
 

                throw EntityUtil.Update(System.Data.Entity.Strings.Update_WorkspaceMismatch, null); 
            }
        }

        private void LoadStateEntry(IEntityStateEntry stateEntry) 
        {
            Debug.Assert(null != stateEntry, "state entry must exist"); 
 
            // make sure the state entry doesn't contain invalid data and register it with the
            // update pipeline 
            ValidateAndRegisterStateEntry(stateEntry);

            // use data structure internal to the update pipeline instead of the raw state entry
            ExtractedStateEntry extractedStateEntry = new ExtractedStateEntry(this, stateEntry); 

            // figure out if this state entry is being handled by a function (stored procedure) or 
            // through dynamic SQL 
            EntitySetBase extent = stateEntry.EntitySet;
            if (null == m_viewLoader.GetFunctionMappingTranslator(extent)) 
            {
                // if there is no function mapping, register a ChangeNode (used for update
                // propagation and dynamic SQL generation)
                ChangeNode changeNode = GetExtentModifications(extent); 
                if (null != extractedStateEntry.Original)
                { 
                    changeNode.Deleted.Add(extractedStateEntry.Original); 
                }
                if (null != extractedStateEntry.Current) 
                {
                    changeNode.Inserted.Add(extractedStateEntry.Current);
                }
            } 
            else
            { 
                // for function updates, store off the extracted state entry in its entirety 
                // (used when producing FunctionUpdateCommands)
                List functionEntries = GetExtentFunctionModifications(extent); 
                functionEntries.Add(extractedStateEntry);
            }
        }
 

 
        ///  
        /// Retrieve a change node for an extent. If none exists, creates and registers a new one.
        ///  
        /// Extent for which to return a change node.
        /// Change node for requested extent.
        internal ChangeNode GetExtentModifications(EntitySetBase extent)
        { 
            EntityUtil.CheckArgumentNull(extent, "extent");
            Debug.Assert(null != m_changes, "(UpdateTranslator/GetChangeNodeForExtent) method called before translator initialized"); 
 
            ChangeNode changeNode;
 
            if (!m_changes.TryGetValue(extent, out changeNode))
            {
                changeNode = new ChangeNode(TypeUsage.Create(extent.ElementType));
                m_changes.Add(extent, changeNode); 
            }
 
            return changeNode; 
        }
 
        /// 
        /// Retrieve a list of state entries being processed by custom user functions.
        /// 
        /// Extent for which to return entries. 
        /// List storing the entries.
        internal List GetExtentFunctionModifications(EntitySetBase extent) 
        { 
            EntityUtil.CheckArgumentNull(extent, "extent");
            Debug.Assert(null != m_functionChanges, "method called before translator initialized"); 

            List entries;

            if (!m_functionChanges.TryGetValue(extent, out entries)) 
            {
                entries = new List(); 
                m_functionChanges.Add(extent, entries); 
            }
 
            return entries;
        }
        #endregion
        #endregion 
    }
 
    ///  
    /// Enumeration of possible operators.
    ///  
    internal enum ModificationOperator : byte
    {
        Update,
        Insert, 
        Delete
    } 
} 

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

using System.Collections.Generic; 
using System.Data.Objects;
using System.Data.Common.Utils;
using System.Data.Common.CommandTrees;
using System.Data.Common; 
using System.Threading;
using System.Collections.ObjectModel; 
using System.Diagnostics; 
using System.Data.Metadata.Edm;
using System.Data.EntityClient; 
using System.Globalization;
using System.Data.Entity;
using System.Linq;
 
namespace System.Data.Mapping.Update.Internal
{ 
    ///  
    /// This class performs to following tasks to persist C-Space changes to the store:
    ///  
    /// Extract changes from the entity state manager
    /// Group changes by C-Space extent
    /// For each affected S-Space table, perform propagation (get changes in S-Space terms)
    /// Merge S-Space inserts and deletes into updates where appropriate 
    /// Produce S-Space commands implementating the modifications (insert, delete and update SQL statements)
    ///  
    ///  
    internal partial class UpdateTranslator
    { 
        #region Constructors
        /// 
        /// Constructs a grouper based on the contents of the given entity state manager.
        ///  
        /// Entity state manager containing changes to be processed.
        /// Metadata workspace. 
        /// Map connection 
        /// Timeout for update commands; null means 'use provider default'
        private UpdateTranslator(IEntityStateManager stateManager, MetadataWorkspace metadataWorkspace, EntityConnection connection, int? commandTimeout) 
        {
            EntityUtil.CheckArgumentNull(stateManager, "stateManager");
            EntityUtil.CheckArgumentNull(metadataWorkspace, "metadataWorkspace");
            EntityUtil.CheckArgumentNull(connection, "connection"); 

            // propagation state 
            m_changes = new Dictionary(); 
            m_functionChanges = new Dictionary>();
            m_stateEntries = new List(); 
            m_knownEntityKeys = new Set();
            m_requiredEntities = new Dictionary();
            m_optionalEntities = new Set();
            m_includedValueEntities = new Set(); 

            // workspace state 
            m_commandTreeContext = new DbQueryCommandTree(metadataWorkspace, DataSpace.CSpace); 
            m_viewLoader = metadataWorkspace.GetUpdateViewLoader();
            m_stateManager = stateManager; 

            // ancillary propagation services
            m_recordConverter = new RecordConverter(this);
            m_constraintValidator = new RelationshipConstraintValidator(this); 

            m_providerServices = DbProviderServices.GetProviderServices(connection.StoreProviderFactory); 
            m_connection = connection; 
            m_commandTimeout = commandTimeout;
 
            // metadata cache
            m_extractorMetadata = new Dictionary(EqualityComparer.Default);

            // key management 
            KeyManager = new KeyManager(this);
            KeyComparer = CompositeKey.CreateComparer(KeyManager); 
        } 

        #endregion 

        #region Fields
        // propagation state
        private readonly Dictionary m_changes; 
        private readonly Dictionary> m_functionChanges;
        private readonly List m_stateEntries; 
        private readonly Set m_knownEntityKeys; 
        private readonly Dictionary m_requiredEntities;
        private readonly Set m_optionalEntities; 
        private readonly Set m_includedValueEntities;

        // workspace state
        private readonly DbCommandTree m_commandTreeContext; 
        private readonly ViewLoader m_viewLoader;
        private readonly IEntityStateManager m_stateManager; 
 
        // ancillary propagation services
        private readonly RecordConverter m_recordConverter; 
        private readonly RelationshipConstraintValidator m_constraintValidator;

        // provider information
        private readonly DbProviderServices m_providerServices; 
        private readonly EntityConnection m_connection;
        private readonly int? m_commandTimeout; 
        private Dictionary m_functionCommandDefinitions; 

        // metadata cache 
        private readonly Dictionary m_extractorMetadata;

        // static members
        private static readonly List s_emptyMemberList = new List(); 
        #endregion
 
        #region Properties 
        /// 
        /// Gets workspace used in this session. 
        /// 
        internal MetadataWorkspace MetadataWorkspace
        {
            get { return m_commandTreeContext.MetadataWorkspace; } 
        }
 
        ///  
        /// Gets key manager that handles interpretation of keys (including resolution of
        /// referential-integrity/common value constraints) 
        /// 
        internal readonly KeyManager KeyManager;

        ///  
        /// Gets the view loader metadata wrapper for the current workspace.
        ///  
        internal ViewLoader ViewLoader 
        {
            get { return m_viewLoader; } 
        }

        /// 
        /// Gets record converter which translates state entry records into propagator results. 
        /// 
        internal RecordConverter RecordConverter 
        { 
            get { return m_recordConverter; }
        } 

        /// 
        /// Gets command timeout for update commands. If null, use default.
        ///  
        internal int? CommandTimeout
        { 
            get { return m_commandTimeout; } 
        }
 
        internal readonly IEqualityComparer KeyComparer;
        #endregion

        #region Methods 
        /// 
        /// Registers any referential constraints contained in the state entry (so that 
        /// constrained members have the same identifier values). Only processes relationships 
        /// with referential constraints defined.
        ///  
        /// State entry
        internal void RegisterReferentialConstraints(IEntityStateEntry stateEntry)
        {
            if (stateEntry.IsRelationship) 
            {
                AssociationSet associationSet = (AssociationSet)stateEntry.EntitySet; 
                if (0 < associationSet.ElementType.ReferentialConstraints.Count) 
                {
                    DbDataRecord record = stateEntry.State == EntityState.Added ? 
                        (DbDataRecord)stateEntry.CurrentValues : stateEntry.OriginalValues;
                    foreach (ReferentialConstraint constraint in associationSet.ElementType.ReferentialConstraints)
                    {
                        // retrieve keys at the ends 
                        EntityKey principalKey = (EntityKey)record[constraint.FromRole.Name];
                        EntityKey dependentKey = (EntityKey)record[constraint.ToRole.Name]; 
 
                        // associate keys, where the from side 'owns' the to side
                        using (ReadOnlyMetadataCollection.Enumerator principalPropertyEnum = constraint.FromProperties.GetEnumerator()) 
                        using (ReadOnlyMetadataCollection.Enumerator dependentPropertyEnum = constraint.ToProperties.GetEnumerator())
                        {
                            while (principalPropertyEnum.MoveNext() && dependentPropertyEnum.MoveNext())
                            { 
                                int principalKeyMemberCount;
                                int dependentKeyMemberCount; 
 
                                // get offsets for from and to key properties
                                int principalOffset = GetKeyMemberOffset(constraint.FromRole, principalPropertyEnum.Current, 
                                    out principalKeyMemberCount);
                                int dependentOffset = GetKeyMemberOffset(constraint.ToRole, dependentPropertyEnum.Current,
                                    out dependentKeyMemberCount);
 
                                long principalIdentifier = this.KeyManager.GetKeyIdentifierForMemberOffset(principalKey, principalOffset, principalKeyMemberCount);
                                long dependentIdentifier = this.KeyManager.GetKeyIdentifierForMemberOffset(dependentKey, dependentOffset, dependentKeyMemberCount); 
 
                                // register equivalence of identifiers
                                this.KeyManager.AddReferentialConstraint(stateEntry, dependentIdentifier, principalIdentifier); 
                            }
                        }
                    }
                } 
            }
        } 
 
        // requires: role must not be null and property must be a key member for the role end
        private static int GetKeyMemberOffset(RelationshipEndMember role, EdmProperty property, out int keyMemberCount) 
        {
            Debug.Assert(null != role);
            Debug.Assert(null != property);
            Debug.Assert(BuiltInTypeKind.RefType == role.TypeUsage.EdmType.BuiltInTypeKind, 
                "relationship ends must be of RefType");
            RefType endType = (RefType)role.TypeUsage.EdmType; 
            Debug.Assert(BuiltInTypeKind.EntityType == endType.ElementType.BuiltInTypeKind, 
                "relationship ends must reference EntityType");
            EntityType entityType = (EntityType)endType.ElementType; 
            keyMemberCount = entityType.KeyMembers.Count;
            return entityType.KeyMembers.IndexOf(property);
        }
 
        /// 
        /// Yields all relationship state entries with the given key as an end. 
        ///  
        /// 
        ///  
        internal IEnumerable GetRelationships(EntityKey entityKey)
        {
            return m_stateManager.FindRelationshipsByKey(entityKey);
        } 

        ///  
        /// Persists stateManager changes to the store. 
        /// 
        /// StateManager containing changes to persist. 
        /// Map adapter requesting the changes.
        /// Total number of state entries affected
        internal static Int32 Update(IEntityStateManager stateManager, IEntityAdapter adapter)
        { 
            IntPtr cookie;
            EntityBid.ScopeEnter(out cookie, ""); 
 
            // provider/connection details
            EntityConnection connection = (EntityConnection)adapter.Connection; 
            MetadataWorkspace metadataWorkspace = connection.GetMetadataWorkspace();
            int? commandTimeout = adapter.CommandTimeout;

            try 
            {
                UpdateTranslator translator = new UpdateTranslator(stateManager, metadataWorkspace, connection, commandTimeout); 
 
                // tracks values for identifiers in this session
                Dictionary identifierValues = new Dictionary(); 

                // tracks values for generated values in this session
                List> generatedValues = new List>();
 
                List orderedCommands = translator.ProduceCommands();
 
                // used to track the source of commands being processed in case an exception is thrown 
                UpdateCommand source = null;
                try 
                {
                    foreach (UpdateCommand command in orderedCommands)
                    {
                        // Remember the data sources so that we can throw meaningful exception 
                        source = command;
                        int rowsAffected = command.Execute(translator, connection, identifierValues, generatedValues); 
                        translator.ValidateRowsAffected(rowsAffected, source); 
                    }
                } 
                catch (Exception e)
                {
                    // we should not be wrapping all exceptions
                    if (UpdateTranslator.RequiresContext(e)) 
                    {
                        throw EntityUtil.Update(System.Data.Entity.Strings.Update_GeneralExecutionException, e, translator.DetermineStateEntriesFromSource(source)); 
                    } 
                    throw;
                } 

                translator.BackPropagateServerGen(generatedValues);

                int totalStateEntries = translator.AcceptChanges(adapter); 

                return totalStateEntries; 
            } 
            finally
            { 
                EntityBid.ScopeLeave(ref cookie);
            }
        }
 
        private List ProduceCommands()
        { 
            // load all modified state entries 
            PullModifiedEntriesFromStateManager();
            PullUnchangedEntriesFromStateManager(); 

            // check constraints
            m_constraintValidator.ValidateConstraints();
            this.KeyManager.ValidateReferentialIntegrityGraphAcyclic(); 

            // gather all commands (aggregate in a dependency orderer to determine operation order 
            IEnumerable dynamicCommands = this.ProduceDynamicCommands(); 
            IEnumerable functionCommands = this.ProduceFunctionCommands();
            UpdateCommandOrderer orderer = new UpdateCommandOrderer(dynamicCommands.Concat(functionCommands), this); 
            List orderedCommands = new List(orderer.Vertices.Count);
            foreach (UpdateCommand[] commands in orderer.TryStagedTopologicalSort())
            {
                // at each depth in the topological sort, apply sorting based on operation, 
                // table/entityset and keys to ensure consistent update ordering
                Array.Sort(commands); 
                orderedCommands.AddRange(commands); 
            }
 
            // if there is a remainder, it suggests a cycle.
            this.ValidateGraphPostSort(orderer);
            return orderedCommands;
        } 

        // effects: given rows affected, throws if the count suggests a concurrency failure. 
        // Throws a concurrency exception based on the current command sources (which allow 
        // us to populated the EntityStateEntries on UpdateException)
        private void ValidateRowsAffected(int rowsAffected, UpdateCommand source) 
        {
            // 0 rows affected indicates a concurrency failure; negative values suggest rowcount is off;
            // positive values suggest at least one row was affected (we generally expect exactly one,
            // but triggers/view logic/logging may change this value) 
            if (0 == rowsAffected)
            { 
                var stateEntries = DetermineStateEntriesFromSource(source); 
                throw EntityUtil.UpdateConcurrency(rowsAffected, null, stateEntries);
            } 
        }

        private IEnumerable DetermineStateEntriesFromSource(UpdateCommand source)
        { 
            if (null == source)
            { 
                return Enumerable.Empty(); 
            }
            return source.GetStateEntries(this); 
        }

        // effects: Given a list of pairs describing the contexts for server generated values and their actual
        // values, backpropagates to the relevant state entries 
        private void BackPropagateServerGen(List> generatedValues)
        { 
            foreach (KeyValuePair generatedValue in generatedValues) 
            {
                PropagatorResult context; 

                // check if a redirect to "owner" result is possible
                if (PropagatorResult.NullIdentifier == generatedValue.Key.Identifier ||
                    !KeyManager.TryGetIdentifierOwner(generatedValue.Key.Identifier, out context)) 
                {
                    // otherwise, just use the straightforward context 
                    context = generatedValue.Key; 
                }
 
                // check that the column is actually mapped to an entity property
                if (context.RecordOrdinal != PropagatorResult.NullOrdinal)
                {
                    object value = generatedValue.Value; 
                    CurrentValueRecord targetRecord = context.Record;
 
                    // determine if type compensation is required 
                    IExtendedDataRecord recordWithMetadata = (IExtendedDataRecord)targetRecord;
                    EdmMember member = recordWithMetadata.DataRecordInfo.FieldMetadata[context.RecordOrdinal].FieldType; 

                    value = value ?? DBNull.Value; // records expect DBNull rather than null
                    value = AlignReturnValue(value, member, context);
                    targetRecord.SetValue(context.RecordOrdinal, value); 
                }
            } 
        } 

        ///  
        /// Aligns a value returned from the store with the expected type for the member.
        /// 
        /// Value to convert.
        /// Metadata for the member being set. 
        /// The context generating the return value.
        /// Converted return value 
        private static object AlignReturnValue(object value, EdmMember member, PropagatorResult context) 
        {
            if (DBNull.Value.Equals(value)) 
            {
                // check if there is a nullability constraint on the value
                if (BuiltInTypeKind.EdmProperty == member.BuiltInTypeKind &&
                    !((EdmProperty)member).Nullable) 
                {
                    throw EntityUtil.Update(System.Data.Entity.Strings.Update_NullReturnValueForNonNullableMember( 
                        member.Name, 
                        member.DeclaringType.FullName), null);
                } 
            }
            else
            {
                // convert the value to the appropriate CLR type 
                Debug.Assert(BuiltInTypeKind.PrimitiveType == member.TypeUsage.EdmType.BuiltInTypeKind,
                    "we only allow return values that are instances of EDM primitive types"); 
                PrimitiveType primitiveType = (PrimitiveType)member.TypeUsage.EdmType; 
                Type clrType = primitiveType.ClrEquivalentType;
 
                try
                {
                    value = Convert.ChangeType(value, clrType, CultureInfo.InvariantCulture);
                } 
                catch (Exception e)
                { 
                    // we should not be wrapping all exceptions 
                    if (UpdateTranslator.RequiresContext(e))
                    { 
                        throw EntityUtil.Update(System.Data.Entity.Strings.Update_ReturnValueHasUnexpectedType(
                            value.GetType().FullName,
                            clrType.FullName,
                            member.Name, 
                            member.DeclaringType.FullName), e);
                    } 
                    throw; 
                }
            } 

            // return the adjusted value
            return value;
        } 

        ///  
        /// Accept changes to entities and relationships processed by this translator instance. 
        /// 
        /// Data adapter 
        /// Number of state entries affected.
        private int AcceptChanges(IEntityAdapter adapter)
        {
            int affectedCount = 0; 
            foreach (IEntityStateEntry stateEntry in m_stateEntries)
            { 
                // only count and accept changes for state entries that are being explicitly modified 
                if (EntityState.Unchanged != stateEntry.State)
                { 
                    if (adapter.AcceptChangesDuringUpdate)
                    {
                        stateEntry.AcceptChanges();
                    } 
                    affectedCount++;
                } 
            } 
            return affectedCount;
        } 

        /// 
        /// Gets extents for which this translator has identified changes to be handled
        /// by the standard update pipeline. 
        /// 
        /// Enumeration of modified C-Space extents. 
        private IEnumerable GetDynamicModifiedExtents() 
        {
            return m_changes.Keys; 
        }

        /// 
        /// Gets extents for which this translator has identified changes to be handled 
        /// by function mappings.
        ///  
        /// Enumreation of modified C-Space extents. 
        private IEnumerable GetFunctionModifiedExtents()
        { 
            return m_functionChanges.Keys;
        }

        ///  
        /// Produce dynamic store commands for this translator's changes.
        ///  
        /// Database commands in a safe order 
        private IEnumerable ProduceDynamicCommands()
        { 
            // Initialize DBCommand update compiler
            UpdateCompiler updateCompiler = new UpdateCompiler(this);

            // Determine affected 
            Set tables = new Set();
 
            foreach (EntitySetBase extent in GetDynamicModifiedExtents()) 
            {
                Set affectedTables = m_viewLoader.GetAffectedTables(extent); 
                //Since these extents don't have Functions defined for update operations,
                //the affected tables should be provided via MSL.
                //If we dont find any throw an exception
                if (affectedTables.Count == 0) 
                {
                    throw EntityUtil.Update(System.Data.Entity.Strings.Update_MappingNotFound( 
                        extent.Name), null /*stateEntries*/); 
                }
 
                foreach (EntitySet table in affectedTables)
                {
                    tables.Add(table);
                } 
            }
 
            // Determine changes to apply to each table 
            List changes = new List(tables.Count);
            foreach (EntitySet table in tables) 
            {
                DbQueryCommandTree umView = m_connection.GetMetadataWorkspace().GetCqtView(table);

                // Propagate changes to root of tree (at which point they are S-Space changes) 
                ChangeNode changeNode = Propagator.Propagate(this, umView);
 
                // Process changes for the table 
                TableChangeProcessor change = new TableChangeProcessor(table);
                foreach (UpdateCommand command in change.CompileCommands(changeNode, updateCompiler)) 
                {
                    yield return command;
                }
            } 
        }
 
        // Generates and caches a command definition for the given function 
        internal DbCommandDefinition GenerateCommandDefinition(StorageFunctionMapping functionMapping)
        { 
            if (null == m_functionCommandDefinitions)
            {
                m_functionCommandDefinitions = new Dictionary();
            } 
            DbCommandDefinition commandDefinition;
            if (!m_functionCommandDefinitions.TryGetValue(functionMapping, out commandDefinition)) 
            { 
                // synthesize a RowType for this mapping
                TypeUsage resultType = null; 
                if (null != functionMapping.ResultBindings && 0 < functionMapping.ResultBindings.Count)
                {
                    List properties = new List(functionMapping.ResultBindings.Count);
                    foreach (StorageFunctionResultBinding resultBinding in functionMapping.ResultBindings) 
                    {
                        properties.Add(new EdmProperty(resultBinding.ColumnName, resultBinding.Property.TypeUsage)); 
                    } 
                    RowType rowType = new RowType(properties);
                    CollectionType collectionType = new CollectionType(rowType); 
                    resultType = TypeUsage.Create(collectionType);
                }

                // construct DbFunctionCommandTree including implict return type 
                DbFunctionCommandTree tree = new DbFunctionCommandTree(m_commandTreeContext.MetadataWorkspace, DataSpace.SSpace,
                    functionMapping.Function, resultType); 
 
                // add function parameters
                foreach (FunctionParameter paramInfo in functionMapping.Function.Parameters) 
                {
                    tree.AddParameter(paramInfo.Name, paramInfo.TypeUsage);
                }
 
                commandDefinition = m_providerServices.CreateCommandDefinition(tree);
            } 
            return commandDefinition; 
        }
 
        // Produces all function commands in a safe order
        private IEnumerable ProduceFunctionCommands()
        {
            foreach (EntitySetBase extent in GetFunctionModifiedExtents()) 
            {
                // Get a handle on the appropriate translator 
                FunctionMappingTranslator translator = m_viewLoader.GetFunctionMappingTranslator(extent); 

                if (null != translator) 
                {
                    // Compile commands
                    foreach (ExtractedStateEntry stateEntry in GetExtentFunctionModifications(extent))
                    { 
                        FunctionUpdateCommand command = translator.Translate(this, stateEntry);
                        if (null != command) 
                        { 
                            yield return command;
                        } 
                    }
                }
            }
        } 

        ///  
        /// Gets a metadata wrapper for the given type. The wrapper makes 
        /// certain tasks in the update pipeline more efficient.
        ///  
        /// Structural type
        /// Metadata wrapper
        internal ExtractorMetadata GetExtractorMetadata(StructuralType type)
        { 
            ExtractorMetadata metadata;
            if (!m_extractorMetadata.TryGetValue(type, out metadata)) 
            { 
                metadata = new ExtractorMetadata(type, this);
                m_extractorMetadata.Add(type, metadata); 
            }
            return metadata;
        }
 
        /// 
        /// Validates dependency graph after sort has been performed to ensure there were no cycles 
        ///  
        /// Dependency orderer graph to validate
        private void ValidateGraphPostSort(UpdateCommandOrderer orderer) 
        {
            Debug.Assert(null != orderer, "Caller must verify parameters are not null");

            if (0 != orderer.Remainder.Count) 
            {
                Set stateEntries = new Set(); 
 
                foreach (UpdateCommand command in orderer.Remainder)
                { 
                    stateEntries.AddRange(command.GetStateEntries(this));
                }

                // throw exception containing all related state entries 
                throw EntityUtil.Update(System.Data.Entity.Strings.Update_ConstraintCycle, null, stateEntries);
            } 
        } 

        ///  
        /// Creates a command in the current context.
        /// 
        /// DbCommand tree
        /// DbCommand produced by the current provider. 
        internal DbCommand CreateCommand(DbModificationCommandTree commandTree)
        { 
            DbCommand command; 
            Debug.Assert(null != m_providerServices, "constructor ensures either the command definition " +
                    "builder or provider service is available"); 
            Debug.Assert(null != m_connection.StoreConnection, "EntityAdapter.Update ensures the store connection is set");
            try
            {
                command = m_providerServices.CreateCommand(commandTree); 
            }
            catch (Exception e) 
            { 
                // we should not be wrapping all exceptions
                if (UpdateTranslator.RequiresContext(e)) 
                {
                    // we don't wan't folks to have to know all the various types of exceptions that can
                    // occur, so we just rethrow a CommandDefinitionException and make whatever we caught
                    // the inner exception of it. 
                    throw EntityUtil.CommandCompilation(System.Data.Entity.Strings.EntityClient_CommandDefinitionPreparationFailed, e);
                } 
                throw; 
            }
            return command; 
        }


        ///  
        /// Determines whether the given exception requires additional context from the update pipeline (in other
        /// words, whether the exception should be wrapped in an UpdateException). 
        ///  
        /// Exception to test.
        /// true if exception should be wrapped; false otherwise 
        internal static bool RequiresContext(Exception e)
        {
            // if the exception isn't catchable, never wrap
            if (!EntityUtil.IsCatchableExceptionType(e)) { return false; } 

            // update and incompatible provider exceptions already contain the necessary context 
            return !(e is UpdateException) && !(e is ProviderIncompatibleException); 
        }
 
        #region Private initialization methods
        /// 
        /// Retrieve all modified entries from the state manager.
        ///  
        private void PullModifiedEntriesFromStateManager()
        { 
            // do a first pass over entries to register referential integrity constraints 
            // for server-generation (only relationship are interesting, and relationships can only
            // be added or deleted) 
            foreach (IEntityStateEntry entry in m_stateManager.GetEntityStateEntries(EntityState.Added | EntityState.Deleted))
            {
                RegisterReferentialConstraints(entry);
            } 

            foreach (IEntityStateEntry modifiedEntry in m_stateManager.GetEntityStateEntries(EntityState.Modified | EntityState.Added | EntityState.Deleted)) 
            { 
                LoadStateEntry(modifiedEntry);
            } 
        }


        ///  
        /// Retrieve all required/optional/value entries into the state manager. These are entries that --
        /// although unmodified -- affect or are affected by updates. 
        ///  
        private void PullUnchangedEntriesFromStateManager()
        { 
            foreach (KeyValuePair required in m_requiredEntities)
            {
                EntityKey key = required.Key;
 
                if (!m_knownEntityKeys.Contains(key))
                { 
                    // pull the value into the translator if we don't already it 
                    IEntityStateEntry requiredEntry;
 
                    if (m_stateManager.TryGetEntityStateEntry(key, out requiredEntry))
                    {
                        // load the object as a no-op update
                        LoadStateEntry(requiredEntry); 
                    }
                    else 
                    { 
                        // throw an exception
                        throw EntityUtil.UpdateMissingEntity(required.Value.Name, TypeHelpers.GetFullName(key.EntityContainerName, key.EntitySetName)); 
                    }
                }
            }
 
            foreach (EntityKey key in m_optionalEntities)
            { 
                if (!m_knownEntityKeys.Contains(key)) 
                {
                    IEntityStateEntry optionalEntry; 

                    if (m_stateManager.TryGetEntityStateEntry(key, out optionalEntry))
                    {
                        // load the object as a no-op update 
                        LoadStateEntry(optionalEntry);
                    } 
                } 
            }
 
            foreach (EntityKey key in m_includedValueEntities)
            {
                if (!m_knownEntityKeys.Contains(key))
                { 
                    IEntityStateEntry valueEntry;
 
                    if (m_stateManager.TryGetEntityStateEntry(key, out valueEntry)) 
                    {
                        // Convert state entry so that its values are known to the update pipeline. 
                        var result = m_recordConverter.ConvertCurrentValuesToPropagatorResult(valueEntry, null);
                    }
                }
            } 
        }
 
        ///  
        /// Validates and tracks a state entry being processed by this translator.
        ///  
        /// 
        private void ValidateAndRegisterStateEntry(IEntityStateEntry stateEntry)
        {
            EntityUtil.CheckArgumentNull(stateEntry, "stateEntry"); 

            EntitySetBase extent = stateEntry.EntitySet; 
            if (null == extent) 
            {
                throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.InvalidStateEntry, 1); 
            }

            // Determine the key. May be null if the state entry does not represent an entity.
            EntityKey entityKey = stateEntry.EntityKey; 
            IExtendedDataRecord record = null;
 
            // verify the structure of the entry values 
            if (0 != ((EntityState.Added | EntityState.Modified | EntityState.Unchanged) & stateEntry.State))
            { 
                // added, modified and unchanged entries have current values
                record = (IExtendedDataRecord)stateEntry.CurrentValues;
                ValidateRecord(extent, record, stateEntry);
            } 
            if (0 != ((EntityState.Modified | EntityState.Deleted | EntityState.Unchanged) & stateEntry.State))
            { 
                // deleted, modified and unchanged entries have original values 
                record = (IExtendedDataRecord)stateEntry.OriginalValues;
                ValidateRecord(extent, record, stateEntry); 
            }
            Debug.Assert(null != record, "every state entry must contain a record");

            // make sure the view loader has loaded all information about this extent 
            m_viewLoader.SyncInitializeEntitySet(extent, this.MetadataWorkspace);
 
            // check for required ends of relationships 
            AssociationSet associationSet = extent as AssociationSet;
            if (null != associationSet) 
            {
                AssociationSetMetadata associationSetMetadata = m_viewLoader.GetAssociationSetMetadata(associationSet);

                if (associationSetMetadata.HasEnds) 
                {
                    foreach (FieldMetadata field in record.DataRecordInfo.FieldMetadata) 
                    { 
                        // ends of relationship record must be EntityKeys
                        EntityKey end = (EntityKey)record.GetValue(field.Ordinal); 

                        // ends of relationships must have AssociationEndMember metadata
                        AssociationEndMember endMetadata = (AssociationEndMember)field.FieldType;
 
                        if (associationSetMetadata.RequiredEnds.Contains(endMetadata))
                        { 
                            if (!m_requiredEntities.ContainsKey(end)) 
                            {
                                m_requiredEntities.Add(end, associationSet); 
                            }
                        }

                        else if (associationSetMetadata.OptionalEnds.Contains(endMetadata)) 
                        {
                            AddValidAncillaryKey(end, m_optionalEntities); 
                        } 

                        else if (associationSetMetadata.IncludedValueEnds.Contains(endMetadata)) 
                        {
                            AddValidAncillaryKey(end, m_includedValueEntities);
                        }
                    } 
                }
 
                // register relationship with validator 
                m_constraintValidator.RegisterAssociation(associationSet, record, stateEntry);
            } 
            else
            {
                // register entity with validator
                m_constraintValidator.RegisterEntity(stateEntry); 
            }
 
            // add to the list of entries being tracked 
            m_stateEntries.Add(stateEntry);
            if (null != (object)entityKey) { m_knownEntityKeys.Add(entityKey); } 
        }

        /// 
        /// effects: given an entity key and a set, adds key to the set iff. the corresponding entity 
        /// is:
        /// 
        ///     not a stub (or 'key') entry, and; 
        ///     not a core element in the update pipeline (it's not being directly modified)
        ///  
        private void AddValidAncillaryKey(EntityKey key, Set keySet)
        {
            // Note: an entity is ancillary iff. it is unchanged (otherwise it is tracked as a "standard" changed entity)
            IEntityStateEntry endEntry; 
            if (m_stateManager.TryGetEntityStateEntry(key, out endEntry) && // make sure the entity is tracked
                !endEntry.IsKeyEntry && // make sure the entity is not a stub 
                endEntry.State == EntityState.Unchanged) // if the entity is being modified, it's already included anyways 
            {
                keySet.Add(key); 
            }
        }

        private void ValidateRecord(EntitySetBase extent, IExtendedDataRecord record, IEntityStateEntry entry) 
        {
            Debug.Assert(null != extent, "must be verified by caller"); 
 
            DataRecordInfo recordInfo;
            if ((null == record) || 
                (null == (recordInfo = record.DataRecordInfo)) ||
                (null == recordInfo.RecordType))
            {
                throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.InvalidStateEntry, 2); 
            }
 
            VerifyExtent(MetadataWorkspace, extent); 

            // additional validation happens lazily as values are loaded from the record 
        }

        // Verifies the given extent is present in the given workspace.
        private static void VerifyExtent(MetadataWorkspace workspace, EntitySetBase extent) 
        {
            // get the container to which the given extent belongs 
            EntityContainer actualContainer = extent.EntityContainer; 

            // try to retrieve the container in the given workspace 
            EntityContainer referenceContainer = null;
            if (null != actualContainer)
            {
                workspace.TryGetEntityContainer( 
                    actualContainer.Name, actualContainer.DataSpace, out referenceContainer);
            } 
 
            // determine if the given extent lives in a container from the given workspace
            // (the item collections for each container are reference equivalent when they are declared in the 
            // same item collection)
            if (null == actualContainer || null == referenceContainer ||
                !Object.ReferenceEquals(actualContainer, referenceContainer))
            { 
                //
 
 

                throw EntityUtil.Update(System.Data.Entity.Strings.Update_WorkspaceMismatch, null); 
            }
        }

        private void LoadStateEntry(IEntityStateEntry stateEntry) 
        {
            Debug.Assert(null != stateEntry, "state entry must exist"); 
 
            // make sure the state entry doesn't contain invalid data and register it with the
            // update pipeline 
            ValidateAndRegisterStateEntry(stateEntry);

            // use data structure internal to the update pipeline instead of the raw state entry
            ExtractedStateEntry extractedStateEntry = new ExtractedStateEntry(this, stateEntry); 

            // figure out if this state entry is being handled by a function (stored procedure) or 
            // through dynamic SQL 
            EntitySetBase extent = stateEntry.EntitySet;
            if (null == m_viewLoader.GetFunctionMappingTranslator(extent)) 
            {
                // if there is no function mapping, register a ChangeNode (used for update
                // propagation and dynamic SQL generation)
                ChangeNode changeNode = GetExtentModifications(extent); 
                if (null != extractedStateEntry.Original)
                { 
                    changeNode.Deleted.Add(extractedStateEntry.Original); 
                }
                if (null != extractedStateEntry.Current) 
                {
                    changeNode.Inserted.Add(extractedStateEntry.Current);
                }
            } 
            else
            { 
                // for function updates, store off the extracted state entry in its entirety 
                // (used when producing FunctionUpdateCommands)
                List functionEntries = GetExtentFunctionModifications(extent); 
                functionEntries.Add(extractedStateEntry);
            }
        }
 

 
        ///  
        /// Retrieve a change node for an extent. If none exists, creates and registers a new one.
        ///  
        /// Extent for which to return a change node.
        /// Change node for requested extent.
        internal ChangeNode GetExtentModifications(EntitySetBase extent)
        { 
            EntityUtil.CheckArgumentNull(extent, "extent");
            Debug.Assert(null != m_changes, "(UpdateTranslator/GetChangeNodeForExtent) method called before translator initialized"); 
 
            ChangeNode changeNode;
 
            if (!m_changes.TryGetValue(extent, out changeNode))
            {
                changeNode = new ChangeNode(TypeUsage.Create(extent.ElementType));
                m_changes.Add(extent, changeNode); 
            }
 
            return changeNode; 
        }
 
        /// 
        /// Retrieve a list of state entries being processed by custom user functions.
        /// 
        /// Extent for which to return entries. 
        /// List storing the entries.
        internal List GetExtentFunctionModifications(EntitySetBase extent) 
        { 
            EntityUtil.CheckArgumentNull(extent, "extent");
            Debug.Assert(null != m_functionChanges, "method called before translator initialized"); 

            List entries;

            if (!m_functionChanges.TryGetValue(extent, out entries)) 
            {
                entries = new List(); 
                m_functionChanges.Add(extent, entries); 
            }
 
            return entries;
        }
        #endregion
        #endregion 
    }
 
    ///  
    /// Enumeration of possible operators.
    ///  
    internal enum ModificationOperator : byte
    {
        Update,
        Insert, 
        Delete
    } 
} 

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