TableChangeProcessor.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

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

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

using System.Data.Metadata.Edm; 
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Common.Utils;
using System.Diagnostics; 
using System.Data.Entity;
using System.Linq; 
namespace System.Data.Mapping.Update.Internal 
{
    ///  
    /// Processes changes applying to a table by merging inserts and deletes into updates
    /// where appropriate.
    /// 
    ///  
    /// This class is essentially responsible for identifying inserts, deletes
    /// and updates in a particular table based on the  
    /// produced by value propagation w.r.t. the update mapping view for that table. 
    /// Assumes the change node includes at most a single insert and at most a single delete
    /// for a given key (where we have both, the change is treated as an update). 
    /// 
    internal class TableChangeProcessor
    {
        #region Constructors 
        /// 
        /// Constructs processor based on the contents of a change node. 
        ///  
        /// Table for which changes are being processed.
        internal TableChangeProcessor(EntitySet table) 
        {
            EntityUtil.CheckArgumentNull(table, "table");

            m_table = table; 

            // cache information about table key 
            m_keyOrdinals = InitializeKeyOrdinals(table); 
        }
        #endregion 

        #region Fields
        private readonly EntitySet m_table;
        private readonly int[] m_keyOrdinals; 
        #endregion
 
        #region Properties 
        /// 
        /// Gets metadata for the table being modified. 
        /// 
        internal EntitySet Table
        {
            get { return m_table; } 
        }
 
        ///  
        /// Gets a map from column ordinal to property descriptions for columns that are components of the table's
        /// primary key. 
        /// 
        internal int[] KeyOrdinals { get { return m_keyOrdinals; } }
        #endregion
 
        #region Methods
        // Determines whether the given ordinal position in the property list 
        // for this table is a key value. 
        internal bool IsKeyProperty(int propertyOrdinal)
        { 
            foreach (int keyOrdinal in m_keyOrdinals)
            {
                if (propertyOrdinal == keyOrdinal) { return true; }
            } 
            return false;
        } 
 
        // Determines which column ordinals in the table are part of the key.
        private static int[] InitializeKeyOrdinals(EntitySet table) 
        {
            EntityType tableType = table.ElementType;
            IList keyMembers = tableType.KeyMembers;
            IBaseList members = TypeHelpers.GetAllStructuralMembers(tableType); 
            int[] keyOrdinals = new int[keyMembers.Count];
 
            for (int keyMemberIndex = 0; keyMemberIndex < keyMembers.Count; keyMemberIndex++) 
            {
                EdmMember keyMember = keyMembers[keyMemberIndex]; 
                keyOrdinals[keyMemberIndex] = members.IndexOf(keyMember);

                Debug.Assert(keyOrdinals[keyMemberIndex] >= 0 && keyOrdinals[keyMemberIndex] < members.Count,
                    "an EntityType key member must also be a member of the entity type"); 
            }
 
            return keyOrdinals; 
        }
 
        // Processes all insert and delete requests in the table's . Inserts
        // and deletes with the same key are merged into updates.
        internal List CompileCommands(ChangeNode changeNode, UpdateCompiler compiler)
        { 
            Set keys = new Set(compiler.m_translator.KeyComparer);
 
            // Retrieve all delete results (original values) and insert results (current values) while 
            // populating a set of all row keys. The set contains a single key per row.
            Dictionary deleteResults = ProcessKeys(compiler, changeNode.Deleted, keys); 
            Dictionary insertResults = ProcessKeys(compiler, changeNode.Inserted, keys);

            List commands = new List(deleteResults.Count + insertResults.Count);
 
            // Examine each row key to see if the row is being deleted, inserted or updated
            foreach (CompositeKey key in keys) 
            { 
                PropagatorResult deleteResult;
                PropagatorResult insertResult; 

                bool hasDelete = deleteResults.TryGetValue(key, out deleteResult);
                bool hasInsert = insertResults.TryGetValue(key, out insertResult);
 
                Debug.Assert(hasDelete || hasInsert, "(update/TableChangeProcessor) m_keys must not contain a value " +
                    "if there is no corresponding insert or delete"); 
 
                try
                { 
                    if (!hasDelete)
                    {
                        // this is an insert
                        commands.Add(compiler.BuildInsertCommand(insertResult, this)); 
                    }
                    else if (!hasInsert) 
                    { 
                        // this is a delete
                        commands.Add(compiler.BuildDeleteCommand(deleteResult, this)); 
                    }
                    else
                    {
                        // this is an update because it has both a delete result and an insert result 
                        UpdateCommand updateCommand = compiler.BuildUpdateCommand(deleteResult, insertResult, this);
                        if (null != updateCommand) 
                        { 
                            // if null is returned, it means it is a no-op update
                            commands.Add(updateCommand); 
                        }
                    }
                }
                catch (Exception e) 
                {
                    if (UpdateTranslator.RequiresContext(e)) 
                    { 
                        // collect state entries in scope for the current compilation
                        List stateEntries = new List(); 
                        if (null != deleteResult)
                        {
                            stateEntries.AddRange(SourceInterpreter.GetAllStateEntries(
                                deleteResult, compiler.m_translator, m_table)); 
                        }
                        if (null != insertResult) 
                        { 
                            stateEntries.AddRange(SourceInterpreter.GetAllStateEntries(
                                insertResult, compiler.m_translator, m_table)); 
                        }

                        throw EntityUtil.Update(System.Data.Entity.Strings.Update_GeneralExecutionException,
                            e, stateEntries); 
                    }
                    throw; 
                } 
            }
 
            return commands;
        }

        // Determines key values for a list of changes. Side effect: populates  which 
        // includes an entry for every key involved in a change.
        private Dictionary ProcessKeys(UpdateCompiler compiler, List changes, Set keys) 
        { 
            Dictionary map = new Dictionary(
                compiler.m_translator.KeyComparer); 

            foreach (PropagatorResult change in changes)
            {
                // Reassign change to row since we cannot modify iteration variable 
                PropagatorResult row = change;
 
                CompositeKey key = new CompositeKey(GetKeyConstants(row)); 

                // Make sure we aren't inserting another row with the same key 
                PropagatorResult other;
                if (map.TryGetValue(key, out other))
                {
                    DiagnoseKeyCollision(compiler, change, key, other); 
                }
 
                map.Add(key, row); 
                keys.Add(key);
            } 

            return map;
        }
 
        private void DiagnoseKeyCollision(UpdateCompiler compiler, PropagatorResult change, CompositeKey key, PropagatorResult other)
        { 
            KeyManager keyManager = compiler.m_translator.KeyManager; 
            CompositeKey otherKey = new CompositeKey(GetKeyConstants(other));
 
            // determine if the conflict is due to shared principal key values
            bool sharedPrincipal = true;
            for (int i = 0; sharedPrincipal && i < key.KeyComponents.Length; i++)
            { 
                int identifier1 = key.KeyComponents[i].Identifier;
                int identifier2 = otherKey.KeyComponents[i].Identifier; 
 
                if (!keyManager.GetPrincipals(identifier1).Intersect(keyManager.GetPrincipals(identifier2)).Any())
                { 
                    sharedPrincipal = false;
                }
            }
 
            if (sharedPrincipal)
            { 
                // if the duplication is due to shared principals, there is a duplicate key exception 
                var stateEntries = SourceInterpreter.GetAllStateEntries(change, compiler.m_translator, m_table)
                    .Concat(SourceInterpreter.GetAllStateEntries(other, compiler.m_translator, m_table)); 
                throw EntityUtil.Update(Strings.Update_DuplicateKeys, null, stateEntries);
            }
            else
            { 
                // if there are no shared principals, it implies that common dependents are the problem
                HashSet commonDependents = null; 
                foreach (PropagatorResult keyValue in key.KeyComponents.Concat(otherKey.KeyComponents)) 
                {
                    var dependents = new HashSet(); 
                    foreach (int dependentId in keyManager.GetDependents(keyValue.Identifier))
                    {
                        PropagatorResult dependentResult;
                        if (keyManager.TryGetIdentifierOwner(dependentId, out dependentResult) && 
                            null != dependentResult.StateEntry)
                        { 
                            dependents.Add(dependentResult.StateEntry); 
                        }
                    } 
                    if (null == commonDependents)
                    {
                        commonDependents = new HashSet(dependents);
                    } 
                    else
                    { 
                        commonDependents.IntersectWith(dependents); 
                    }
                } 

                // to ensure the exception shape is consistent with constraint violations discovered while processing
                // commands (a more conventional scenario in which different tables are contributing principal values)
                // wrap a DataConstraintException in an UpdateException 
                throw EntityUtil.Update(Strings.Update_GeneralExecutionException,
                    EntityUtil.Constraint(Strings.Update_ReferentialConstraintIntegrityViolation), commonDependents); 
            } 
        }
 
        // Extracts key constants from the given row.
        private PropagatorResult[] GetKeyConstants(PropagatorResult row)
        {
            PropagatorResult[] keyConstants = new PropagatorResult[m_keyOrdinals.Length]; 
            for (int i = 0; i < m_keyOrdinals.Length; i++)
            { 
                PropagatorResult constant = row.GetMemberValue(m_keyOrdinals[i]); 

                keyConstants[i] = constant; 
            }
            return keyConstants;
        }
        #endregion 
    }
} 

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

using System.Data.Metadata.Edm; 
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Common.Utils;
using System.Diagnostics; 
using System.Data.Entity;
using System.Linq; 
namespace System.Data.Mapping.Update.Internal 
{
    ///  
    /// Processes changes applying to a table by merging inserts and deletes into updates
    /// where appropriate.
    /// 
    ///  
    /// This class is essentially responsible for identifying inserts, deletes
    /// and updates in a particular table based on the  
    /// produced by value propagation w.r.t. the update mapping view for that table. 
    /// Assumes the change node includes at most a single insert and at most a single delete
    /// for a given key (where we have both, the change is treated as an update). 
    /// 
    internal class TableChangeProcessor
    {
        #region Constructors 
        /// 
        /// Constructs processor based on the contents of a change node. 
        ///  
        /// Table for which changes are being processed.
        internal TableChangeProcessor(EntitySet table) 
        {
            EntityUtil.CheckArgumentNull(table, "table");

            m_table = table; 

            // cache information about table key 
            m_keyOrdinals = InitializeKeyOrdinals(table); 
        }
        #endregion 

        #region Fields
        private readonly EntitySet m_table;
        private readonly int[] m_keyOrdinals; 
        #endregion
 
        #region Properties 
        /// 
        /// Gets metadata for the table being modified. 
        /// 
        internal EntitySet Table
        {
            get { return m_table; } 
        }
 
        ///  
        /// Gets a map from column ordinal to property descriptions for columns that are components of the table's
        /// primary key. 
        /// 
        internal int[] KeyOrdinals { get { return m_keyOrdinals; } }
        #endregion
 
        #region Methods
        // Determines whether the given ordinal position in the property list 
        // for this table is a key value. 
        internal bool IsKeyProperty(int propertyOrdinal)
        { 
            foreach (int keyOrdinal in m_keyOrdinals)
            {
                if (propertyOrdinal == keyOrdinal) { return true; }
            } 
            return false;
        } 
 
        // Determines which column ordinals in the table are part of the key.
        private static int[] InitializeKeyOrdinals(EntitySet table) 
        {
            EntityType tableType = table.ElementType;
            IList keyMembers = tableType.KeyMembers;
            IBaseList members = TypeHelpers.GetAllStructuralMembers(tableType); 
            int[] keyOrdinals = new int[keyMembers.Count];
 
            for (int keyMemberIndex = 0; keyMemberIndex < keyMembers.Count; keyMemberIndex++) 
            {
                EdmMember keyMember = keyMembers[keyMemberIndex]; 
                keyOrdinals[keyMemberIndex] = members.IndexOf(keyMember);

                Debug.Assert(keyOrdinals[keyMemberIndex] >= 0 && keyOrdinals[keyMemberIndex] < members.Count,
                    "an EntityType key member must also be a member of the entity type"); 
            }
 
            return keyOrdinals; 
        }
 
        // Processes all insert and delete requests in the table's . Inserts
        // and deletes with the same key are merged into updates.
        internal List CompileCommands(ChangeNode changeNode, UpdateCompiler compiler)
        { 
            Set keys = new Set(compiler.m_translator.KeyComparer);
 
            // Retrieve all delete results (original values) and insert results (current values) while 
            // populating a set of all row keys. The set contains a single key per row.
            Dictionary deleteResults = ProcessKeys(compiler, changeNode.Deleted, keys); 
            Dictionary insertResults = ProcessKeys(compiler, changeNode.Inserted, keys);

            List commands = new List(deleteResults.Count + insertResults.Count);
 
            // Examine each row key to see if the row is being deleted, inserted or updated
            foreach (CompositeKey key in keys) 
            { 
                PropagatorResult deleteResult;
                PropagatorResult insertResult; 

                bool hasDelete = deleteResults.TryGetValue(key, out deleteResult);
                bool hasInsert = insertResults.TryGetValue(key, out insertResult);
 
                Debug.Assert(hasDelete || hasInsert, "(update/TableChangeProcessor) m_keys must not contain a value " +
                    "if there is no corresponding insert or delete"); 
 
                try
                { 
                    if (!hasDelete)
                    {
                        // this is an insert
                        commands.Add(compiler.BuildInsertCommand(insertResult, this)); 
                    }
                    else if (!hasInsert) 
                    { 
                        // this is a delete
                        commands.Add(compiler.BuildDeleteCommand(deleteResult, this)); 
                    }
                    else
                    {
                        // this is an update because it has both a delete result and an insert result 
                        UpdateCommand updateCommand = compiler.BuildUpdateCommand(deleteResult, insertResult, this);
                        if (null != updateCommand) 
                        { 
                            // if null is returned, it means it is a no-op update
                            commands.Add(updateCommand); 
                        }
                    }
                }
                catch (Exception e) 
                {
                    if (UpdateTranslator.RequiresContext(e)) 
                    { 
                        // collect state entries in scope for the current compilation
                        List stateEntries = new List(); 
                        if (null != deleteResult)
                        {
                            stateEntries.AddRange(SourceInterpreter.GetAllStateEntries(
                                deleteResult, compiler.m_translator, m_table)); 
                        }
                        if (null != insertResult) 
                        { 
                            stateEntries.AddRange(SourceInterpreter.GetAllStateEntries(
                                insertResult, compiler.m_translator, m_table)); 
                        }

                        throw EntityUtil.Update(System.Data.Entity.Strings.Update_GeneralExecutionException,
                            e, stateEntries); 
                    }
                    throw; 
                } 
            }
 
            return commands;
        }

        // Determines key values for a list of changes. Side effect: populates  which 
        // includes an entry for every key involved in a change.
        private Dictionary ProcessKeys(UpdateCompiler compiler, List changes, Set keys) 
        { 
            Dictionary map = new Dictionary(
                compiler.m_translator.KeyComparer); 

            foreach (PropagatorResult change in changes)
            {
                // Reassign change to row since we cannot modify iteration variable 
                PropagatorResult row = change;
 
                CompositeKey key = new CompositeKey(GetKeyConstants(row)); 

                // Make sure we aren't inserting another row with the same key 
                PropagatorResult other;
                if (map.TryGetValue(key, out other))
                {
                    DiagnoseKeyCollision(compiler, change, key, other); 
                }
 
                map.Add(key, row); 
                keys.Add(key);
            } 

            return map;
        }
 
        private void DiagnoseKeyCollision(UpdateCompiler compiler, PropagatorResult change, CompositeKey key, PropagatorResult other)
        { 
            KeyManager keyManager = compiler.m_translator.KeyManager; 
            CompositeKey otherKey = new CompositeKey(GetKeyConstants(other));
 
            // determine if the conflict is due to shared principal key values
            bool sharedPrincipal = true;
            for (int i = 0; sharedPrincipal && i < key.KeyComponents.Length; i++)
            { 
                int identifier1 = key.KeyComponents[i].Identifier;
                int identifier2 = otherKey.KeyComponents[i].Identifier; 
 
                if (!keyManager.GetPrincipals(identifier1).Intersect(keyManager.GetPrincipals(identifier2)).Any())
                { 
                    sharedPrincipal = false;
                }
            }
 
            if (sharedPrincipal)
            { 
                // if the duplication is due to shared principals, there is a duplicate key exception 
                var stateEntries = SourceInterpreter.GetAllStateEntries(change, compiler.m_translator, m_table)
                    .Concat(SourceInterpreter.GetAllStateEntries(other, compiler.m_translator, m_table)); 
                throw EntityUtil.Update(Strings.Update_DuplicateKeys, null, stateEntries);
            }
            else
            { 
                // if there are no shared principals, it implies that common dependents are the problem
                HashSet commonDependents = null; 
                foreach (PropagatorResult keyValue in key.KeyComponents.Concat(otherKey.KeyComponents)) 
                {
                    var dependents = new HashSet(); 
                    foreach (int dependentId in keyManager.GetDependents(keyValue.Identifier))
                    {
                        PropagatorResult dependentResult;
                        if (keyManager.TryGetIdentifierOwner(dependentId, out dependentResult) && 
                            null != dependentResult.StateEntry)
                        { 
                            dependents.Add(dependentResult.StateEntry); 
                        }
                    } 
                    if (null == commonDependents)
                    {
                        commonDependents = new HashSet(dependents);
                    } 
                    else
                    { 
                        commonDependents.IntersectWith(dependents); 
                    }
                } 

                // to ensure the exception shape is consistent with constraint violations discovered while processing
                // commands (a more conventional scenario in which different tables are contributing principal values)
                // wrap a DataConstraintException in an UpdateException 
                throw EntityUtil.Update(Strings.Update_GeneralExecutionException,
                    EntityUtil.Constraint(Strings.Update_ReferentialConstraintIntegrityViolation), commonDependents); 
            } 
        }
 
        // Extracts key constants from the given row.
        private PropagatorResult[] GetKeyConstants(PropagatorResult row)
        {
            PropagatorResult[] keyConstants = new PropagatorResult[m_keyOrdinals.Length]; 
            for (int i = 0; i < m_keyOrdinals.Length; i++)
            { 
                PropagatorResult constant = row.GetMemberValue(m_keyOrdinals[i]); 

                keyConstants[i] = constant; 
            }
            return keyConstants;
        }
        #endregion 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK