UpdateCompiler.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataEntity / System / Data / Map / Update / Internal / UpdateCompiler.cs / 2 / UpdateCompiler.cs

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

using System.Data.Metadata.Edm; 
// For the purposes of the update compiler, the member name fully describes the member
// within the table entity set.
// It's convenient to use 'string' to represent the member, because it allows us to
// painlessly associate members of the transient extent (the table in C-Space) with 
// the real extent (the table in S-Space).
using System.Data.Common; 
using System.Collections.Generic; 
using System.Text;
using System.Diagnostics; 
using System.Globalization;
using System.Data.Common.Utils;
using System.Data.SqlClient.SqlGen;
using System.Data.Common.CommandTrees; 
using System.Data.Entity;
namespace System.Data.Mapping.Update.Internal 
{ 
    /// 
    /// This class implements compilation of DML operation requests to some 
    /// format (e.g. canonical query tree or T-SQL)
    /// 
    internal sealed class UpdateCompiler
    { 
        #region Constructors
        ///  
        /// Initialize an update compiler. 
        /// 
        /// Update context. 
        internal UpdateCompiler(UpdateTranslator translator)
        {
            m_translator = translator;
        } 
        #endregion
 
        #region Fields 
        internal readonly UpdateTranslator m_translator;
        private const string s_targetVarName = "target"; 
        #endregion

        /// 
        /// Builds a delete command. 
        /// 
        /// Value of the row being deleted. 
        /// Context for the table containing row. 
        /// Delete command.
        internal UpdateCommand BuildDeleteCommand(PropagatorResult oldRow, TableChangeProcessor processor) 
        {
            // If we're deleting a row, the row must always be touched
            bool rowMustBeTouched = true;
 
            // Initialize DML command tree
            DbDeleteCommandTree commandTree = new DbDeleteCommandTree(m_translator.MetadataWorkspace, DataSpace.CSpace); 
            SetTarget(processor, commandTree); 

            // Create delete predicate 
            commandTree.Predicate = BuildPredicate(commandTree, oldRow, null, processor, ref rowMustBeTouched);

            // Set command
            // Initialize delete command 
            UpdateCommand command = new DynamicUpdateCommand(processor, m_translator, ModificationOperator.Delete, oldRow, null, commandTree, null);
 
            return command; 
        }
 
        /// 
        /// Builds an update command.
        /// 
        /// Old value of the row being updated. 
        /// New value for the row being updated.
        /// Context for the table containing row. 
        /// Update command. 
        internal UpdateCommand BuildUpdateCommand(PropagatorResult oldRow,
            PropagatorResult newRow, TableChangeProcessor processor) 
        {
            // If we're updating a row, the row may not need to be touched (e.g., no concurrency validation required)
            bool rowMustBeTouched = false;
 
            // Initialize DML command tree
            DbUpdateCommandTree commandTree = new DbUpdateCommandTree(m_translator.MetadataWorkspace, DataSpace.CSpace); 
            SetTarget(processor, commandTree); 

            // Create update predicate 
            commandTree.Predicate = BuildPredicate(commandTree, oldRow, newRow, processor, ref rowMustBeTouched);

            // Add set clauses
            Dictionary outputIdentifiers; 
            DbExpression returning;
            List setClauses = new List(); 
            foreach (DbModificationClause clause in BuildSetClauses( 
                commandTree, newRow, processor, /* insertMode */ false, out outputIdentifiers, out returning,
                ref rowMustBeTouched)) 
            {
                setClauses.Add(clause);
            }
            commandTree.InitializeSetClauses(setClauses); 

            // Construct predicate identifying the row to modify 
            commandTree.Predicate = BuildPredicate(commandTree, oldRow, newRow, processor, ref rowMustBeTouched); 

            // Determine if there is nothing to do (i.e., no values to set, 
            // no computed columns, and no concurrency validation required)
            if (!rowMustBeTouched && 0 == commandTree.SetClauses.Count)
            {
                return null; 
            }
 
            // Set the returning parameters 
            if (null != returning)
            { 
                commandTree.Returning = returning;
            }

            // Create command 
            UpdateCommand command = new DynamicUpdateCommand(processor, m_translator, ModificationOperator.Update, oldRow, newRow, commandTree, outputIdentifiers);
 
            return command; 
        }
 
        /// 
        /// Builds insert command.
        /// 
        /// Row to insert. 
        /// Context for the table we're inserting into.
        /// Insert command. 
        internal UpdateCommand BuildInsertCommand(PropagatorResult newRow, TableChangeProcessor processor) 
        {
            // Initialize DML command tree 
            DbInsertCommandTree commandTree = new DbInsertCommandTree(m_translator.MetadataWorkspace, DataSpace.CSpace);
            SetTarget(processor, commandTree);

            // Add set clauses 
            Dictionary outputIdentifiers;
            DbExpression returning; 
            bool rowMustBeTouched = true; // for inserts, the row must always be touched 
            List setClauses = new List();
            foreach (DbModificationClause clause in BuildSetClauses(commandTree, newRow, processor, /* insertMode */ true, out outputIdentifiers, 
                out returning, ref rowMustBeTouched))
            {
                setClauses.Add(clause);
            } 
            commandTree.InitializeSetClauses(setClauses);
 
            // Set the returning parameters 
            if (null != returning)
            { 
                commandTree.Returning = returning;
            }

            // Create command 
            UpdateCommand command = new DynamicUpdateCommand(processor, m_translator, ModificationOperator.Insert, null, newRow, commandTree, outputIdentifiers);
 
            return command; 
        }
 
        /// 
        /// Determines column/value used to set values for a row.
        /// 
        ///  
        /// The following columns are not included in the result:
        ///  
        /// Keys (if  is set) 
        /// Values flagged 'preserve' (these are values the propagator claims are
        /// untouched) 
        /// Server generated values.
        /// 
        /// 
        /// Command tree hosting the set parameters. 
        /// Row containing values to set.
        /// Context for table. 
        /// Determines whether key columns and 'preserve' columns are 
        /// omitted from the list.
        /// Dictionary listing server generated identifiers. 
        /// DbExpression describing result projection for server generated values.
        /// Indicates whether the row must be touched
        /// because it produces a value (e.g. computed)
        /// Column value pairs. 
        private IEnumerable BuildSetClauses(DbModificationCommandTree commandTree, PropagatorResult row,
            TableChangeProcessor processor, bool insertMode, out Dictionary outputIdentifiers, out DbExpression returning, 
            ref bool rowMustBeTouched) 
        {
            Dictionary setClauses = new Dictionary(); 
            List> returningArguments = new List>();
            outputIdentifiers = new Dictionary();

            // Determine which flags indicate a property should be omitted from the set list. 
            PropagatorFlags omitMask = insertMode ? PropagatorFlags.NoFlags :
                PropagatorFlags.Preserve | PropagatorFlags.Unknown; 
 
            for (int propertyOrdinal = 0; propertyOrdinal < processor.Table.ElementType.Properties.Count; propertyOrdinal++)
            { 
                EdmProperty property = processor.Table.ElementType.Properties[propertyOrdinal];

                // Type members and result values are ordinally aligned
                PropagatorResult propertyResult = row.GetMemberValue(propertyOrdinal); 

                if (PropagatorResult.NullIdentifier != propertyResult.Identifier) 
                { 
                    // retrieve principal value
                    propertyResult = propertyResult.ReplicateResultWithNewValue( 
                        m_translator.KeyManager.GetPrincipalValue(propertyResult));
                }

                bool omitFromSetList = false; 

                Debug.Assert(propertyResult.IsSimple); 
 
                // Determine if this is a key value
                bool isKey = false; 
                for (int i = 0; i < processor.KeyOrdinals.Length; i++)
                {
                    if (processor.KeyOrdinals[i] == propertyOrdinal)
                    { 
                        isKey = true;
                        break; 
                    } 
                }
 
                // check if this value should be omitted
                PropagatorFlags flags = PropagatorFlags.NoFlags;
                if (!insertMode && isKey)
                { 
                    // Keys are only set for inserts
                    omitFromSetList = true; 
                } 
                else
                { 
                    // See if this value has been marked up with some context. If so, add the flag information
                    // from the markup. Markup includes information about whether the property is a concurrency value,
                    // whether it is known (it may be a property that is preserved across an update for instance)
                    flags |= propertyResult.PropagatorFlags; 
                }
 
                // Determine if this value is server-generated 
                StoreGeneratedPattern genPattern = MetadataHelper.GetStoreGeneratedPattern(property);
                bool isServerGen = genPattern == StoreGeneratedPattern.Computed || 
                    (insertMode && genPattern == StoreGeneratedPattern.Identity);
                if (isServerGen)
                {
                    DbPropertyExpression propertyExpression = commandTree.CreatePropertyExpression( 
                        property, commandTree.Target.Variable);
                    returningArguments.Add(new KeyValuePair(property.Name, propertyExpression)); 
 
                    // check if this is a server generated identifier
                    Int64 identifier = propertyResult.Identifier; 
                    if (PropagatorResult.NullIdentifier != identifier)
                    {
                        if (m_translator.KeyManager.HasPrincipals(identifier))
                        { 
                            throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_GeneratedDependent(property.Name));
                        } 
                        outputIdentifiers.Add(identifier, property.Name); 

                        // If this property maps an identifier (in the update pipeline) it may 
                        // also be a store key. If so, the pattern had better be "Identity"
                        // since otherwise we're dealing with a mutable key.
                        if (genPattern != StoreGeneratedPattern.Identity &&
                            processor.IsKeyProperty(propertyOrdinal)) 
                        {
                            throw EntityUtil.NotSupported(System.Data.Entity.Strings.Update_NotSupportedComputedKeyColumn( 
                                EdmProviderManifest.StoreGeneratedPatternFacetName, 
                                XmlConstants.Computed,
                                XmlConstants.Identity, 
                                property.Name,
                                property.DeclaringType.FullName));
                        }
                    } 
                }
 
                if (PropagatorFlags.NoFlags != (flags & (omitMask))) 
                {
                    // column value matches "omit" pattern, therefore should not be set 
                    omitFromSetList = true;
                }
                else if (isServerGen)
                { 
                    // column value does not match "omit" pattern, but it is server generated
                    // so it cannot be set 
                    omitFromSetList = true; 

                    // if the row has a modified value overridden by server gen, 
                    // it must still be touched in order to retrieve the value
                    rowMustBeTouched = true;
                }
 
                // make the user is not updating an identity value
                if (!omitFromSetList && !insertMode && genPattern == StoreGeneratedPattern.Identity) 
                { 
                    throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_ModifyingIdentityColumn(
                        XmlConstants.Identity, 
                        property.Name,
                        property.DeclaringType.FullName));
                }
 
                if (!omitFromSetList) { setClauses.Add(property, propertyResult); }
            } 
 
            // Construct returning projection
            if (0 < returningArguments.Count) 
            {
                returning = commandTree.CreateNewRowExpression(returningArguments);
            }
            else 
            {
                returning = null; 
            } 

            // Construct clauses corresponding to the set clauses 
            List result = new List(setClauses.Count);
            foreach (KeyValuePair setClause in setClauses)
            {
                EdmProperty property = setClause.Key; 

                result.Add(new DbSetClause(commandTree, 
                    GeneratePropertyExpression(commandTree, setClause.Key), 
                    GenerateValueExpression(commandTree, setClause.Key, setClause.Value)));
            } 

            return result;
        }
 
        /// 
        /// Determines predicate used to identify a row in a table. 
        ///  
        /// 
        /// Columns are included in the list when: 
        /// 
        /// They are keys for the table
        /// They are concurrency values
        ///  
        /// 
        /// Command tree hosting the predicate 
        /// Values for the row being located. 
        /// Values being updated (may be null).
        /// Context for the table containing the row. 
        /// Output parameter indicating whether a row must be touched
        /// (whether it's being modified or not) because it contains a concurrency value
        /// Column/value pairs.
        private DbExpression BuildPredicate(DbModificationCommandTree commandTree, PropagatorResult referenceRow, PropagatorResult current, 
            TableChangeProcessor processor, ref bool rowMustBeTouched)
        { 
            Dictionary whereClauses = new Dictionary(); 

            // add all concurrency tokens (note that keys are always concurrency tokens as well) 
            int propertyOrdinal = 0;
            foreach (EdmProperty member in processor.Table.ElementType.Members)
            {
                // members and result values are ordinally aligned 
                PropagatorResult expectedValue = referenceRow.GetMemberValue(propertyOrdinal);
                PropagatorResult newValue = null == current ? null : current.GetMemberValue(propertyOrdinal); 
 
                // check if the rowMustBeTouched value should be set to true (if it isn't already
                // true and we've come across a concurrency value) 
                if (!rowMustBeTouched &&
                    (HasFlag(expectedValue, PropagatorFlags.ConcurrencyValue) ||
                     HasFlag(newValue, PropagatorFlags.ConcurrencyValue)))
                { 
                    rowMustBeTouched = true;
                } 
 
                // determine if this is a concurrency value
                if (!whereClauses.ContainsKey(member) && // don't add to the set clause twice 
                    (HasFlag(expectedValue, PropagatorFlags.ConcurrencyValue | PropagatorFlags.Key) ||
                     HasFlag(newValue, PropagatorFlags.ConcurrencyValue | PropagatorFlags.Key))) // tagged as concurrency value
                {
                    whereClauses.Add(member, expectedValue); 
                }
                propertyOrdinal++; 
            } 

            // Build a binary AND expression tree from the clauses 
            DbExpression predicate = null;
            foreach (KeyValuePair clause in whereClauses)
            {
                DbExpression clauseExpression = GenerateEqualityExpression(commandTree, clause.Key, clause.Value); 
                if (null == predicate) { predicate = clauseExpression; }
                else { predicate = commandTree.CreateAndExpression(predicate, clauseExpression); } 
            } 

            Debug.Assert(null != predicate, "some predicate term must exist"); 

            return predicate;
        }
 
        // Effects: given a "clause" in the form of a property/value pair, produces an equality expression. If the
        // value is null, creates an IsNull expression 
        // Requires: all arguments are set 
        private DbExpression GenerateEqualityExpression(DbModificationCommandTree commandTree, EdmProperty property, PropagatorResult value)
        { 
            Debug.Assert(null != commandTree && null != property && null != value);

            DbExpression propertyExpression = GeneratePropertyExpression(commandTree, property);
            if (value.IsNull) 
            {
                return commandTree.CreateIsNullExpression(propertyExpression); 
            } 
            if (!TypeSemantics.IsEqualComparable(property.TypeUsage))
            { 
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_NonEquatableColumnTypeInClause(
                    property.Name, property.DeclaringType.Name));
            }
            return commandTree.CreateEqualsExpression(propertyExpression, GenerateValueExpression(commandTree, property, value)); 
        }
 
        // Effects: given a property, produces a property expression 
        // Requires: all arguments are set
        private static DbExpression GeneratePropertyExpression(DbModificationCommandTree commandTree, EdmProperty property) 
        {
            Debug.Assert(null != commandTree && null != property);

            return commandTree.CreatePropertyExpression(property, commandTree.Target.Variable); 
        }
 
        // Effects: given a propagator result, produces a constant expression describing that value. 
        // Requires: all arguments are set, and the value must be simple (scalar)
        private DbExpression GenerateValueExpression(DbCommandTree commandTree, EdmProperty property, PropagatorResult value) 
        {
            Debug.Assert(null != commandTree && null != value && value.IsSimple && null != property);

            if (value.IsNull) 
            {
                return commandTree.CreateNullExpression(Helper.GetModelTypeUsage(property)); 
            } 
            object principalValue = m_translator.KeyManager.GetPrincipalValue(value);
            return commandTree.CreateConstantExpression(principalValue, Helper.GetModelTypeUsage(property)); 
        }

        // Effects: returns true iff. the input propagator result has some flag defined in "flags"
        // Requires: input is set 
        private static bool HasFlag(PropagatorResult input, PropagatorFlags flags)
        { 
            if (null == input) { return false; } 
            return (PropagatorFlags.NoFlags != (flags & input.PropagatorFlags));
        } 

        // Effects: initializes the target (table being modified) for the given DML command tree according
        // to the table managed by the processor.
        // Requires: all arguments set 
        private static void SetTarget(TableChangeProcessor processor, DbModificationCommandTree commandTree)
        { 
            Debug.Assert(null != processor && null != commandTree); 

            // use a fixed var name since the command trees all have exactly one binding 
            commandTree.Target = commandTree.CreateExpressionBinding(
                commandTree.CreateScanExpression(processor.Table), s_targetVarName);
        }
    } 
}

// 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; 
// For the purposes of the update compiler, the member name fully describes the member
// within the table entity set.
// It's convenient to use 'string' to represent the member, because it allows us to
// painlessly associate members of the transient extent (the table in C-Space) with 
// the real extent (the table in S-Space).
using System.Data.Common; 
using System.Collections.Generic; 
using System.Text;
using System.Diagnostics; 
using System.Globalization;
using System.Data.Common.Utils;
using System.Data.SqlClient.SqlGen;
using System.Data.Common.CommandTrees; 
using System.Data.Entity;
namespace System.Data.Mapping.Update.Internal 
{ 
    /// 
    /// This class implements compilation of DML operation requests to some 
    /// format (e.g. canonical query tree or T-SQL)
    /// 
    internal sealed class UpdateCompiler
    { 
        #region Constructors
        ///  
        /// Initialize an update compiler. 
        /// 
        /// Update context. 
        internal UpdateCompiler(UpdateTranslator translator)
        {
            m_translator = translator;
        } 
        #endregion
 
        #region Fields 
        internal readonly UpdateTranslator m_translator;
        private const string s_targetVarName = "target"; 
        #endregion

        /// 
        /// Builds a delete command. 
        /// 
        /// Value of the row being deleted. 
        /// Context for the table containing row. 
        /// Delete command.
        internal UpdateCommand BuildDeleteCommand(PropagatorResult oldRow, TableChangeProcessor processor) 
        {
            // If we're deleting a row, the row must always be touched
            bool rowMustBeTouched = true;
 
            // Initialize DML command tree
            DbDeleteCommandTree commandTree = new DbDeleteCommandTree(m_translator.MetadataWorkspace, DataSpace.CSpace); 
            SetTarget(processor, commandTree); 

            // Create delete predicate 
            commandTree.Predicate = BuildPredicate(commandTree, oldRow, null, processor, ref rowMustBeTouched);

            // Set command
            // Initialize delete command 
            UpdateCommand command = new DynamicUpdateCommand(processor, m_translator, ModificationOperator.Delete, oldRow, null, commandTree, null);
 
            return command; 
        }
 
        /// 
        /// Builds an update command.
        /// 
        /// Old value of the row being updated. 
        /// New value for the row being updated.
        /// Context for the table containing row. 
        /// Update command. 
        internal UpdateCommand BuildUpdateCommand(PropagatorResult oldRow,
            PropagatorResult newRow, TableChangeProcessor processor) 
        {
            // If we're updating a row, the row may not need to be touched (e.g., no concurrency validation required)
            bool rowMustBeTouched = false;
 
            // Initialize DML command tree
            DbUpdateCommandTree commandTree = new DbUpdateCommandTree(m_translator.MetadataWorkspace, DataSpace.CSpace); 
            SetTarget(processor, commandTree); 

            // Create update predicate 
            commandTree.Predicate = BuildPredicate(commandTree, oldRow, newRow, processor, ref rowMustBeTouched);

            // Add set clauses
            Dictionary outputIdentifiers; 
            DbExpression returning;
            List setClauses = new List(); 
            foreach (DbModificationClause clause in BuildSetClauses( 
                commandTree, newRow, processor, /* insertMode */ false, out outputIdentifiers, out returning,
                ref rowMustBeTouched)) 
            {
                setClauses.Add(clause);
            }
            commandTree.InitializeSetClauses(setClauses); 

            // Construct predicate identifying the row to modify 
            commandTree.Predicate = BuildPredicate(commandTree, oldRow, newRow, processor, ref rowMustBeTouched); 

            // Determine if there is nothing to do (i.e., no values to set, 
            // no computed columns, and no concurrency validation required)
            if (!rowMustBeTouched && 0 == commandTree.SetClauses.Count)
            {
                return null; 
            }
 
            // Set the returning parameters 
            if (null != returning)
            { 
                commandTree.Returning = returning;
            }

            // Create command 
            UpdateCommand command = new DynamicUpdateCommand(processor, m_translator, ModificationOperator.Update, oldRow, newRow, commandTree, outputIdentifiers);
 
            return command; 
        }
 
        /// 
        /// Builds insert command.
        /// 
        /// Row to insert. 
        /// Context for the table we're inserting into.
        /// Insert command. 
        internal UpdateCommand BuildInsertCommand(PropagatorResult newRow, TableChangeProcessor processor) 
        {
            // Initialize DML command tree 
            DbInsertCommandTree commandTree = new DbInsertCommandTree(m_translator.MetadataWorkspace, DataSpace.CSpace);
            SetTarget(processor, commandTree);

            // Add set clauses 
            Dictionary outputIdentifiers;
            DbExpression returning; 
            bool rowMustBeTouched = true; // for inserts, the row must always be touched 
            List setClauses = new List();
            foreach (DbModificationClause clause in BuildSetClauses(commandTree, newRow, processor, /* insertMode */ true, out outputIdentifiers, 
                out returning, ref rowMustBeTouched))
            {
                setClauses.Add(clause);
            } 
            commandTree.InitializeSetClauses(setClauses);
 
            // Set the returning parameters 
            if (null != returning)
            { 
                commandTree.Returning = returning;
            }

            // Create command 
            UpdateCommand command = new DynamicUpdateCommand(processor, m_translator, ModificationOperator.Insert, null, newRow, commandTree, outputIdentifiers);
 
            return command; 
        }
 
        /// 
        /// Determines column/value used to set values for a row.
        /// 
        ///  
        /// The following columns are not included in the result:
        ///  
        /// Keys (if  is set) 
        /// Values flagged 'preserve' (these are values the propagator claims are
        /// untouched) 
        /// Server generated values.
        /// 
        /// 
        /// Command tree hosting the set parameters. 
        /// Row containing values to set.
        /// Context for table. 
        /// Determines whether key columns and 'preserve' columns are 
        /// omitted from the list.
        /// Dictionary listing server generated identifiers. 
        /// DbExpression describing result projection for server generated values.
        /// Indicates whether the row must be touched
        /// because it produces a value (e.g. computed)
        /// Column value pairs. 
        private IEnumerable BuildSetClauses(DbModificationCommandTree commandTree, PropagatorResult row,
            TableChangeProcessor processor, bool insertMode, out Dictionary outputIdentifiers, out DbExpression returning, 
            ref bool rowMustBeTouched) 
        {
            Dictionary setClauses = new Dictionary(); 
            List> returningArguments = new List>();
            outputIdentifiers = new Dictionary();

            // Determine which flags indicate a property should be omitted from the set list. 
            PropagatorFlags omitMask = insertMode ? PropagatorFlags.NoFlags :
                PropagatorFlags.Preserve | PropagatorFlags.Unknown; 
 
            for (int propertyOrdinal = 0; propertyOrdinal < processor.Table.ElementType.Properties.Count; propertyOrdinal++)
            { 
                EdmProperty property = processor.Table.ElementType.Properties[propertyOrdinal];

                // Type members and result values are ordinally aligned
                PropagatorResult propertyResult = row.GetMemberValue(propertyOrdinal); 

                if (PropagatorResult.NullIdentifier != propertyResult.Identifier) 
                { 
                    // retrieve principal value
                    propertyResult = propertyResult.ReplicateResultWithNewValue( 
                        m_translator.KeyManager.GetPrincipalValue(propertyResult));
                }

                bool omitFromSetList = false; 

                Debug.Assert(propertyResult.IsSimple); 
 
                // Determine if this is a key value
                bool isKey = false; 
                for (int i = 0; i < processor.KeyOrdinals.Length; i++)
                {
                    if (processor.KeyOrdinals[i] == propertyOrdinal)
                    { 
                        isKey = true;
                        break; 
                    } 
                }
 
                // check if this value should be omitted
                PropagatorFlags flags = PropagatorFlags.NoFlags;
                if (!insertMode && isKey)
                { 
                    // Keys are only set for inserts
                    omitFromSetList = true; 
                } 
                else
                { 
                    // See if this value has been marked up with some context. If so, add the flag information
                    // from the markup. Markup includes information about whether the property is a concurrency value,
                    // whether it is known (it may be a property that is preserved across an update for instance)
                    flags |= propertyResult.PropagatorFlags; 
                }
 
                // Determine if this value is server-generated 
                StoreGeneratedPattern genPattern = MetadataHelper.GetStoreGeneratedPattern(property);
                bool isServerGen = genPattern == StoreGeneratedPattern.Computed || 
                    (insertMode && genPattern == StoreGeneratedPattern.Identity);
                if (isServerGen)
                {
                    DbPropertyExpression propertyExpression = commandTree.CreatePropertyExpression( 
                        property, commandTree.Target.Variable);
                    returningArguments.Add(new KeyValuePair(property.Name, propertyExpression)); 
 
                    // check if this is a server generated identifier
                    Int64 identifier = propertyResult.Identifier; 
                    if (PropagatorResult.NullIdentifier != identifier)
                    {
                        if (m_translator.KeyManager.HasPrincipals(identifier))
                        { 
                            throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_GeneratedDependent(property.Name));
                        } 
                        outputIdentifiers.Add(identifier, property.Name); 

                        // If this property maps an identifier (in the update pipeline) it may 
                        // also be a store key. If so, the pattern had better be "Identity"
                        // since otherwise we're dealing with a mutable key.
                        if (genPattern != StoreGeneratedPattern.Identity &&
                            processor.IsKeyProperty(propertyOrdinal)) 
                        {
                            throw EntityUtil.NotSupported(System.Data.Entity.Strings.Update_NotSupportedComputedKeyColumn( 
                                EdmProviderManifest.StoreGeneratedPatternFacetName, 
                                XmlConstants.Computed,
                                XmlConstants.Identity, 
                                property.Name,
                                property.DeclaringType.FullName));
                        }
                    } 
                }
 
                if (PropagatorFlags.NoFlags != (flags & (omitMask))) 
                {
                    // column value matches "omit" pattern, therefore should not be set 
                    omitFromSetList = true;
                }
                else if (isServerGen)
                { 
                    // column value does not match "omit" pattern, but it is server generated
                    // so it cannot be set 
                    omitFromSetList = true; 

                    // if the row has a modified value overridden by server gen, 
                    // it must still be touched in order to retrieve the value
                    rowMustBeTouched = true;
                }
 
                // make the user is not updating an identity value
                if (!omitFromSetList && !insertMode && genPattern == StoreGeneratedPattern.Identity) 
                { 
                    throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_ModifyingIdentityColumn(
                        XmlConstants.Identity, 
                        property.Name,
                        property.DeclaringType.FullName));
                }
 
                if (!omitFromSetList) { setClauses.Add(property, propertyResult); }
            } 
 
            // Construct returning projection
            if (0 < returningArguments.Count) 
            {
                returning = commandTree.CreateNewRowExpression(returningArguments);
            }
            else 
            {
                returning = null; 
            } 

            // Construct clauses corresponding to the set clauses 
            List result = new List(setClauses.Count);
            foreach (KeyValuePair setClause in setClauses)
            {
                EdmProperty property = setClause.Key; 

                result.Add(new DbSetClause(commandTree, 
                    GeneratePropertyExpression(commandTree, setClause.Key), 
                    GenerateValueExpression(commandTree, setClause.Key, setClause.Value)));
            } 

            return result;
        }
 
        /// 
        /// Determines predicate used to identify a row in a table. 
        ///  
        /// 
        /// Columns are included in the list when: 
        /// 
        /// They are keys for the table
        /// They are concurrency values
        ///  
        /// 
        /// Command tree hosting the predicate 
        /// Values for the row being located. 
        /// Values being updated (may be null).
        /// Context for the table containing the row. 
        /// Output parameter indicating whether a row must be touched
        /// (whether it's being modified or not) because it contains a concurrency value
        /// Column/value pairs.
        private DbExpression BuildPredicate(DbModificationCommandTree commandTree, PropagatorResult referenceRow, PropagatorResult current, 
            TableChangeProcessor processor, ref bool rowMustBeTouched)
        { 
            Dictionary whereClauses = new Dictionary(); 

            // add all concurrency tokens (note that keys are always concurrency tokens as well) 
            int propertyOrdinal = 0;
            foreach (EdmProperty member in processor.Table.ElementType.Members)
            {
                // members and result values are ordinally aligned 
                PropagatorResult expectedValue = referenceRow.GetMemberValue(propertyOrdinal);
                PropagatorResult newValue = null == current ? null : current.GetMemberValue(propertyOrdinal); 
 
                // check if the rowMustBeTouched value should be set to true (if it isn't already
                // true and we've come across a concurrency value) 
                if (!rowMustBeTouched &&
                    (HasFlag(expectedValue, PropagatorFlags.ConcurrencyValue) ||
                     HasFlag(newValue, PropagatorFlags.ConcurrencyValue)))
                { 
                    rowMustBeTouched = true;
                } 
 
                // determine if this is a concurrency value
                if (!whereClauses.ContainsKey(member) && // don't add to the set clause twice 
                    (HasFlag(expectedValue, PropagatorFlags.ConcurrencyValue | PropagatorFlags.Key) ||
                     HasFlag(newValue, PropagatorFlags.ConcurrencyValue | PropagatorFlags.Key))) // tagged as concurrency value
                {
                    whereClauses.Add(member, expectedValue); 
                }
                propertyOrdinal++; 
            } 

            // Build a binary AND expression tree from the clauses 
            DbExpression predicate = null;
            foreach (KeyValuePair clause in whereClauses)
            {
                DbExpression clauseExpression = GenerateEqualityExpression(commandTree, clause.Key, clause.Value); 
                if (null == predicate) { predicate = clauseExpression; }
                else { predicate = commandTree.CreateAndExpression(predicate, clauseExpression); } 
            } 

            Debug.Assert(null != predicate, "some predicate term must exist"); 

            return predicate;
        }
 
        // Effects: given a "clause" in the form of a property/value pair, produces an equality expression. If the
        // value is null, creates an IsNull expression 
        // Requires: all arguments are set 
        private DbExpression GenerateEqualityExpression(DbModificationCommandTree commandTree, EdmProperty property, PropagatorResult value)
        { 
            Debug.Assert(null != commandTree && null != property && null != value);

            DbExpression propertyExpression = GeneratePropertyExpression(commandTree, property);
            if (value.IsNull) 
            {
                return commandTree.CreateIsNullExpression(propertyExpression); 
            } 
            if (!TypeSemantics.IsEqualComparable(property.TypeUsage))
            { 
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_NonEquatableColumnTypeInClause(
                    property.Name, property.DeclaringType.Name));
            }
            return commandTree.CreateEqualsExpression(propertyExpression, GenerateValueExpression(commandTree, property, value)); 
        }
 
        // Effects: given a property, produces a property expression 
        // Requires: all arguments are set
        private static DbExpression GeneratePropertyExpression(DbModificationCommandTree commandTree, EdmProperty property) 
        {
            Debug.Assert(null != commandTree && null != property);

            return commandTree.CreatePropertyExpression(property, commandTree.Target.Variable); 
        }
 
        // Effects: given a propagator result, produces a constant expression describing that value. 
        // Requires: all arguments are set, and the value must be simple (scalar)
        private DbExpression GenerateValueExpression(DbCommandTree commandTree, EdmProperty property, PropagatorResult value) 
        {
            Debug.Assert(null != commandTree && null != value && value.IsSimple && null != property);

            if (value.IsNull) 
            {
                return commandTree.CreateNullExpression(Helper.GetModelTypeUsage(property)); 
            } 
            object principalValue = m_translator.KeyManager.GetPrincipalValue(value);
            return commandTree.CreateConstantExpression(principalValue, Helper.GetModelTypeUsage(property)); 
        }

        // Effects: returns true iff. the input propagator result has some flag defined in "flags"
        // Requires: input is set 
        private static bool HasFlag(PropagatorResult input, PropagatorFlags flags)
        { 
            if (null == input) { return false; } 
            return (PropagatorFlags.NoFlags != (flags & input.PropagatorFlags));
        } 

        // Effects: initializes the target (table being modified) for the given DML command tree according
        // to the table managed by the processor.
        // Requires: all arguments set 
        private static void SetTarget(TableChangeProcessor processor, DbModificationCommandTree commandTree)
        { 
            Debug.Assert(null != processor && null != commandTree); 

            // use a fixed var name since the command trees all have exactly one binding 
            commandTree.Target = commandTree.CreateExpressionBinding(
                commandTree.CreateScanExpression(processor.Table), s_targetVarName);
        }
    } 
}

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