updatecommandorderer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntity / System / Data / Map / Update / Internal / updatecommandorderer.cs / 1 / 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 (Int64 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 (Int64 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() 
        { 
            // build up 'required' and 'producing' graphs
            KeyToListMap requiredMap = new KeyToListMap(EqualityComparer.Default); 
            KeyToListMap producedMap = new KeyToListMap(EqualityComparer.Default);

            foreach (UpdateCommand command in this.Vertices)
            { 
                List required;
                List produced; 
                command.GetRequiredAndProducedEntities(_translator, out required, out produced); 
                foreach (EntityKey key in required)
                { 
                    requiredMap.Add(key, command);
                }
                foreach (EntityKey key in produced)
                { 
                    producedMap.Add(key, command);
                } 
            } 

            // build up dependencies 
            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.
//---------------------------------------------------------------------- 
// 
//      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 (Int64 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 (Int64 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() 
        { 
            // build up 'required' and 'producing' graphs
            KeyToListMap requiredMap = new KeyToListMap(EqualityComparer.Default); 
            KeyToListMap producedMap = new KeyToListMap(EqualityComparer.Default);

            foreach (UpdateCommand command in this.Vertices)
            { 
                List required;
                List produced; 
                command.GetRequiredAndProducedEntities(_translator, out required, out produced); 
                foreach (EntityKey key in required)
                { 
                    requiredMap.Add(key, command);
                }
                foreach (EntityKey key in produced)
                { 
                    producedMap.Add(key, command);
                } 
            } 

            // build up dependencies 
            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