updatecommandorderer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Map / Update / Internal / updatecommandorderer.cs / 1305376 / updatecommandorderer.cs

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

using System.Collections.Generic; 
using System.Data.Common.Utils;
using System.Data.Metadata.Edm;
using System.Linq;
using System.Diagnostics; 
namespace System.Data.Mapping.Update.Internal
{ 
    internal class UpdateCommandOrderer : Graph 
    {
        ///  
        /// Gets comparer used to resolve identifiers to actual 'owning' key values (e.g. across referential constraints)
        /// 
        private readonly ForeignKeyValueComparer _keyComparer;
 
        /// 
        /// Maps from tables to all "source" referential constraints (where the table declares 
        /// foreign keys) 
        /// 
        private readonly KeyToListMap _sourceMap; 

        /// 
        /// Maps from tables to all "target" referential constraints (where the table is
        /// referenced by a foreign key) 
        /// 
        private readonly KeyToListMap _targetMap; 
 
        /// 
        /// Tracks whether any function commands exist in the current payload. 
        /// 
        private readonly bool _hasFunctionCommands;

        ///  
        /// Gets translator producing this graph.
        ///  
        private readonly UpdateTranslator _translator; 

        internal UpdateCommandOrderer(IEnumerable commands, UpdateTranslator translator) 
            : base(EqualityComparer.Default)
        {
            _translator = translator;
            _keyComparer = new ForeignKeyValueComparer(_translator.KeyComparer); 

            HashSet tables = new HashSet(); 
            HashSet containers = new HashSet(); 

            // add all vertices (one vertex for every command) 
            foreach (UpdateCommand command in commands)
            {
                if (null != command.Table)
                { 
                    tables.Add(command.Table);
                    containers.Add(command.Table.EntityContainer); 
                } 
                AddVertex(command);
                if (command.Kind == UpdateCommandKind.Function) 
                {
                    _hasFunctionCommands = true;
                }
            } 

            // figure out which foreign keys are interesting in this scope 
            InitializeForeignKeyMaps(containers, tables, out _sourceMap, out _targetMap); 

            // add edges for each ordering dependency amongst the commands 
            AddServerGenDependencies();
            AddForeignKeyDependencies();
            if (_hasFunctionCommands)
            { 
                AddModelDependencies();
            } 
        } 

        private static void InitializeForeignKeyMaps(HashSet containers, HashSet tables, out KeyToListMap sourceMap, out KeyToListMap targetMap) 
        {
            sourceMap = new KeyToListMap(EqualityComparer.Default);
            targetMap = new KeyToListMap(EqualityComparer.Default);
 
            // Retrieve relationship ends from each container to populate edges in dependency
            // graph 
            foreach (EntityContainer container in containers) 
            {
                foreach (EntitySetBase extent in container.BaseEntitySets) 
                {
                    AssociationSet associationSet = extent as AssociationSet;

                    if (null != associationSet) 
                    {
                        AssociationSetEnd source = null; 
                        AssociationSetEnd target = null; 

                        var ends = associationSet.AssociationSetEnds; 

                        if (2 == ends.Count)
                        {
                            // source is equivalent to the "to" end of relationship, target is "from" 
                            AssociationType associationType = associationSet.ElementType;
                            bool constraintFound = false; 
                            ReferentialConstraint fkConstraint = null; 
                            foreach (ReferentialConstraint constraint in associationType.ReferentialConstraints)
                            { 
                                if (constraintFound) { Debug.Fail("relationship set should have at most one constraint"); }
                                else { constraintFound = true; }
                                source = associationSet.AssociationSetEnds[constraint.ToRole.Name];
                                target = associationSet.AssociationSetEnds[constraint.FromRole.Name]; 
                                fkConstraint = constraint;
                            } 
 
                            Debug.Assert(constraintFound && null != target && null != source, "relationship set must have at least one constraint");
                            // only understand binary (foreign key) relationships between entity sets 
                            if (null != target && null != source)
                            {
                                if (tables.Contains(target.EntitySet)&&
                                    tables.Contains(source.EntitySet)) 
                                {
                                    // Remember metadata 
                                    sourceMap.Add(source.EntitySet, fkConstraint); 
                                    targetMap.Add(target.EntitySet, fkConstraint);
                                } 
                            }
                        }
                    }
                } 
            }
        } 
 
        // Adds edges to dependency graph for server-generated values.
        // 
        // Determines which commands produce identifiers (key components) and which commands
        // consume them. Producers are potentially edge predecessors and consumers are potentially
        // edge successors. The command objects report the identifiers they produce (OutputIdentifiers)
        // and the identifiers they consume (InputIdentifiers) 
        private void AddServerGenDependencies()
        { 
            // Identify all "shared" output parameters (e.g., SQL Server identifiers) 
            Dictionary predecessors = new Dictionary();
            foreach (UpdateCommand command in this.Vertices) 
            {
                foreach (int output in command.OutputIdentifiers)
                {
                    try 
                    {
                        predecessors.Add(output, command); 
                    } 
                    catch (ArgumentException duplicateKey)
                    { 
                        // throw an exception indicating that a key value is generated in two locations
                        // in the store
                        throw EntityUtil.Update(System.Data.Entity.Strings.Update_AmbiguousServerGenIdentifier, duplicateKey, command.GetStateEntries(_translator));
                    } 
                }
            } 
 
            // Identify all dependent input parameters
            foreach (UpdateCommand command in this.Vertices) 
            {
                foreach (int input in command.InputIdentifiers)
                {
                    UpdateCommand from; 
                    if (predecessors.TryGetValue(input, out from))
                    { 
                        AddEdge(from, command); 
                    }
                } 
            }
        }

        // Adds edges to dependency graph based on foreign keys. 
        private void AddForeignKeyDependencies()
        { 
            KeyToListMap predecessors = DetermineForeignKeyPredecessors(); 
            AddForeignKeyEdges(predecessors);
        } 

        // Finds all successors to the given predecessors and registers the resulting dependency edges in this
        // graph.
        // 
        // - Commands (updates or inserts) inserting FK "sources" (referencing foreign key)
        // - Commands (updates or deletes) deleting FK "targets" (referenced by the foreign key) 
        // 
        // To avoid violating constraints, FK references must be created before their referees, and
        // cannot be deleted before their references. 
        private void AddForeignKeyEdges(KeyToListMap predecessors)
        {
            foreach (DynamicUpdateCommand command in this.Vertices.OfType())
            { 
                // register all source successors
                if (ModificationOperator.Update == command.Operator || 
                    ModificationOperator.Insert == command.Operator) 
                {
                    foreach (ReferentialConstraint fkConstraint in _sourceMap.EnumerateValues(command.Table)) 
                    {
                        ForeignKeyValue fk;
                        if (ForeignKeyValue.TryCreateSourceKey(fkConstraint, command.CurrentValues, true, out fk))
                        { 
                            // if this is an update and the source key is unchanged, there is no
                            // need to add a dependency (from the perspective of the target, the update 
                            // is a no-op) 
                            ForeignKeyValue originalFK;
                            if (ModificationOperator.Update != command.Operator || 
                                !ForeignKeyValue.TryCreateSourceKey(fkConstraint, command.OriginalValues, true, out originalFK) ||
                                !_keyComparer.Equals(originalFK, fk))
                            {
                                foreach (UpdateCommand predecessor in predecessors.EnumerateValues(fk)) 
                                {
                                    // don't add self-edges for FK dependencies, since a single operation 
                                    // in the store is atomic 
                                    if (predecessor != command)
                                    { 
                                        AddEdge(predecessor, command);
                                    }
                                }
                            } 
                        }
                    } 
                } 

                // register all target successors 
                if (ModificationOperator.Update == command.Operator ||
                    ModificationOperator.Delete == command.Operator)
                {
                    foreach (ReferentialConstraint fkConstraint in _targetMap.EnumerateValues(command.Table)) 
                    {
                        ForeignKeyValue fk; 
                        if (ForeignKeyValue.TryCreateTargetKey(fkConstraint, command.OriginalValues, false, out fk)) 
                        {
                            // if this is an update and the target key is unchanged, there is no 
                            // need to add a dependency (from the perspective of the source, the update
                            // is a no-op)
                            ForeignKeyValue currentFK;
                            if (ModificationOperator.Update != command.Operator || 
                                !ForeignKeyValue.TryCreateTargetKey(fkConstraint, command.CurrentValues, false, out currentFK) ||
                                !_keyComparer.Equals(currentFK, fk)) 
                            { 

                                foreach (UpdateCommand predecessor in predecessors.EnumerateValues(fk)) 
                                {
                                    // don't add self-edges for FK dependencies, since a single operation
                                    // in the store is atomic
                                    if (predecessor != command) 
                                    {
                                        AddEdge(predecessor, command); 
                                    } 
                                }
                            } 
                        }
                    }
                }
            } 
        }
 
        // Builds a map from foreign key instances to commands, with an entry for every command that may need to 
        // precede some other operation.
        // 
        // Predecessor commands must precede other commands using those values. There are two kinds of
        // predecessor:
        //
        // - Commands (updates or inserts) inserting FK "targets" (referenced by the foreign key) 
        // - Commands (updates or deletes) deleting FK "sources" (referencing the foreign key)
        // 
        // To avoid violating constraints, FK values must be created before they are referenced, and 
        // cannot be deleted before their references
        private KeyToListMap DetermineForeignKeyPredecessors() 
        {
            KeyToListMap predecessors = new KeyToListMap(
                _keyComparer);
 
            foreach (DynamicUpdateCommand command in this.Vertices.OfType())
            { 
                if (ModificationOperator.Update == command.Operator || 
                    ModificationOperator.Insert == command.Operator)
                { 
                    foreach (ReferentialConstraint fkConstraint in _targetMap.EnumerateValues(command.Table))
                    {
                        ForeignKeyValue fk;
                        if (ForeignKeyValue.TryCreateTargetKey(fkConstraint, command.CurrentValues, true, out fk)) 
                        {
                            // if this is an update and the target key is unchanged, there is no 
                            // need to add a dependency (from the perspective of the target, the update 
                            // is a no-op)
                            ForeignKeyValue originalFK; 
                            if (ModificationOperator.Update != command.Operator ||
                                !ForeignKeyValue.TryCreateTargetKey(fkConstraint, command.OriginalValues, true, out originalFK) ||
                                !_keyComparer.Equals(originalFK, fk))
                            { 
                                predecessors.Add(fk, command);
                            } 
                        } 
                    }
                } 

                // register all source predecessors
                if (ModificationOperator.Update == command.Operator ||
                    ModificationOperator.Delete == command.Operator) 
                {
                    foreach (ReferentialConstraint fkConstraint in _sourceMap.EnumerateValues(command.Table)) 
                    { 
                        ForeignKeyValue fk;
                        if (ForeignKeyValue.TryCreateSourceKey(fkConstraint, command.OriginalValues, false, out fk)) 
                        {
                            // if this is an update and the source key is unchanged, there is no
                            // need to add a dependency (from the perspective of the source, the update
                            // is a no-op) 
                            ForeignKeyValue currentFK;
                            if (ModificationOperator.Update != command.Operator || 
                                !ForeignKeyValue.TryCreateSourceKey(fkConstraint, command.CurrentValues, false, out currentFK) || 
                                !_keyComparer.Equals(currentFK, fk))
                            { 
                                predecessors.Add(fk, command);
                            }
                        }
                    } 
                }
            } 
            return predecessors; 
        }
 
        /// 
        /// For function commands, we infer constraints based on relationships and entities. For instance,
        /// we always insert an entity before inserting a relationship referencing that entity. When dynamic
        /// and function UpdateCommands are mixed, we also fall back on this same interpretation. 
        /// 
        private void AddModelDependencies() 
        { 
            KeyToListMap addedEntities = new KeyToListMap(EqualityComparer.Default);
            KeyToListMap deletedEntities = new KeyToListMap(EqualityComparer.Default); 
            KeyToListMap addedRelationships = new KeyToListMap(EqualityComparer.Default);
            KeyToListMap deletedRelationships = new KeyToListMap(EqualityComparer.Default);

            foreach (UpdateCommand command in this.Vertices) 
            {
                command.GetRequiredAndProducedEntities(_translator, addedEntities, deletedEntities, addedRelationships, deletedRelationships); 
            } 

            // Add entities before adding dependent relationships 
            AddModelDependencies(producedMap: addedEntities, requiredMap: addedRelationships);

            // Delete dependent relationships before deleting entities
            AddModelDependencies(producedMap: deletedRelationships, requiredMap: deletedEntities); 
        }
 
        private void AddModelDependencies(KeyToListMap producedMap, KeyToListMap requiredMap) 
        {
            foreach (var keyAndCommands in requiredMap.KeyValuePairs) 
            {
                EntityKey key = keyAndCommands.Key;
                List commandsRequiringKey = keyAndCommands.Value;
 
                foreach (UpdateCommand commandProducingKey in producedMap.EnumerateValues(key))
                { 
                    foreach (UpdateCommand commandRequiringKey in commandsRequiringKey) 
                    {
                        // command cannot depend on itself and only function commands 
                        // need to worry about model dependencies (dynamic commands know about foreign keys)
                        if (!object.ReferenceEquals(commandProducingKey, commandRequiringKey) &&
                            (commandProducingKey.Kind == UpdateCommandKind.Function ||
                             commandRequiringKey.Kind == UpdateCommandKind.Function)) 
                        {
                            // add a dependency 
                            AddEdge(commandProducingKey, commandRequiringKey); 
                        }
                    } 
                }
            }
        }
 
        /// 
        /// Describes an update command's foreign key (source or target) 
        ///  
        private struct ForeignKeyValue
        { 
            /// 
            /// Constructor
            /// 
            /// Sets Metadata 
            /// Record containing key value
            /// Indicates whether the source or target end of the constraint 
            /// is being pulled 
            /// Indicates whether this is an insert dependency or a delete
            /// dependency 
            private ForeignKeyValue(ReferentialConstraint metadata, PropagatorResult record,
                bool isTarget, bool isInsert)
            {
                Metadata = metadata; 

                // construct key 
                IList keyProperties = isTarget ? metadata.FromProperties : 
                    metadata.ToProperties;
                PropagatorResult[] keyValues = new PropagatorResult[keyProperties.Count]; 
                bool hasNullMember = false;
                for (int i = 0; i < keyValues.Length; i++)
                {
                    keyValues[i] = record.GetMemberValue(keyProperties[i]); 
                    if (keyValues[i].IsNull)
                    { 
                        hasNullMember = true; 
                        break;
                    } 
                }

                if (hasNullMember)
                { 
                    // set key to null to indicate that it is not behaving as a key
                    // (in SQL, keys with null components do not participate in constraints) 
                    Key = null; 
                }
                else 
                {
                    Key = new CompositeKey(keyValues);
                }
 
                IsInsert = isInsert;
            } 
 
            /// 
            /// Initialize foreign key object for the target of a foreign key. 
            /// 
            /// Sets Metadata
            /// Record containing key value
            /// Indicates whether the key value is being inserted or deleted 
            /// Outputs key object
            /// true if the record contains key values for this constraint; false otherwise 
            internal static bool TryCreateTargetKey(ReferentialConstraint metadata, PropagatorResult record, bool isInsert, out ForeignKeyValue key) 
            {
                key = new ForeignKeyValue(metadata, record, true, isInsert); 
                if (null == key.Key)
                {
                    return false;
                } 
                return true;
            } 
 
            /// 
            /// Initialize foreign key object for the source of a foreign key. 
            /// 
            /// Sets Metadata
            /// Record containing key value
            /// Indicates whether the key value is being inserted or deleted 
            /// Outputs key object
            /// true if the record contains key values for this constraint; false otherwise 
            internal static bool TryCreateSourceKey(ReferentialConstraint metadata, PropagatorResult record, bool isInsert, out ForeignKeyValue key) 
            {
                key = new ForeignKeyValue(metadata, record, false, isInsert); 
                if (null == key.Key)
                {
                    return false;
                } 
                return true;
            } 
 
            /// 
            /// Foreign key metadata. 
            /// 
            internal readonly ReferentialConstraint Metadata;

            ///  
            /// Foreign key value.
            ///  
            internal readonly CompositeKey Key; 

            ///  
            /// Indicates whether this is an inserted or deleted key value.
            /// 
            internal readonly bool IsInsert;
        } 

        ///  
        /// Equality comparer for ForeignKey class. 
        /// 
        private class ForeignKeyValueComparer : IEqualityComparer 
        {
            private readonly IEqualityComparer _baseComparer;

            internal ForeignKeyValueComparer(IEqualityComparer baseComparer) 
            {
                _baseComparer = EntityUtil.CheckArgumentNull(baseComparer, "baseComparer"); 
            } 

            public bool Equals(ForeignKeyValue x, ForeignKeyValue y) 
            {
                return x.IsInsert == y.IsInsert && x.Metadata == y.Metadata &&
                    _baseComparer.Equals(x.Key, y.Key);
            } 

            public int GetHashCode(ForeignKeyValue obj) 
            { 
                return _baseComparer.GetHashCode(obj.Key);
            } 
        }
    }
}

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