internal DynamicUpdateCommand(TableChangeProcessor processor, UpdateTranslator translator, ModificationOperator op,
            PropagatorResult originalValues, PropagatorResult currentValues, DbModificationCommandTree tree,
            Dictionary<int, string> outputIdentifiers)
            : base(originalValues, currentValues)
        {
            m_processor = EntityUtil.CheckArgumentNull(processor, "processor");
            m_operator = op;
            m_modificationCommandTree = EntityUtil.CheckArgumentNull(tree, "commandTree");
            m_outputIdentifiers = outputIdentifiers; // may be null (not all commands have output identifiers)

            // initialize identifier information (supports lateral propagation of server gen values)
            if (ModificationOperator.Insert == op || ModificationOperator.Update == op)
            {
                const int capacity = 2; // "average" number of identifiers per row
                m_inputIdentifiers = new List<KeyValuePair<int ,DbSetClause>>(capacity);

                foreach (KeyValuePair<EdmMember, PropagatorResult> member in
                    Helper.PairEnumerations(TypeHelpers.GetAllStructuralMembers(this.CurrentValues.StructuralType),
                                             this.CurrentValues.GetMemberValues()))
                {
                    DbSetClause setter;
                    int identifier = member.Value.Identifier;

                    if (PropagatorResult.NullIdentifier != identifier &&
                        TryGetSetterExpression(tree, member.Key, op, out setter)) // can find corresponding setter
                    {
                        foreach (int principal in translator.KeyManager.GetPrincipals(identifier))
                        {
                            m_inputIdentifiers.Add(new KeyValuePair<int, DbSetClause>(principal, setter));
                        }
                    }
                }
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Builds insert command.
        /// </summary>
        /// <param name="newRow">Row to insert.</param>
        /// <param name="processor">Context for the table we're inserting into.</param>
        /// <returns>Insert command.</returns>
        internal UpdateCommand BuildInsertCommand(PropagatorResult newRow, TableChangeProcessor processor)
        {
            // Bind the insert target
            DbExpressionBinding target = GetTarget(processor);

            // Create set clauses and returning parameter
            Dictionary <int, string> outputIdentifiers;
            DbExpression             returning;
            bool rowMustBeTouched = true; // for inserts, the row must always be touched
            List <DbModificationClause> setClauses = new List <DbModificationClause>();

            foreach (DbModificationClause clause in BuildSetClauses(target, newRow, null, processor, /* insertMode */ true, out outputIdentifiers,
                                                                    out returning, ref rowMustBeTouched))
            {
                setClauses.Add(clause);
            }

            // Initialize DML command tree
            DbInsertCommandTree commandTree =
                new DbInsertCommandTree(m_translator.MetadataWorkspace, DataSpace.SSpace, target, setClauses.AsReadOnly(), returning);

            // Create command
            UpdateCommand command = new DynamicUpdateCommand(processor, m_translator, ModificationOperator.Insert, null, newRow, commandTree, outputIdentifiers);

            return(command);
        }
Esempio n. 3
0
        // 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 DbExpressionBinding GetTarget(TableChangeProcessor processor)
        {
            Debug.Assert(null != processor);

            // use a fixed var name since the command trees all have exactly one binding
            return(processor.Table.Scan().BindAs(s_targetVarName));
        }
Esempio n. 4
0
        internal DynamicUpdateCommand(TableChangeProcessor processor, UpdateTranslator translator, ModificationOperator op,
                                      PropagatorResult originalValues, PropagatorResult currentValues, DbModificationCommandTree tree,
                                      Dictionary <int, string> outputIdentifiers)
            : base(originalValues, currentValues)
        {
            m_processor = EntityUtil.CheckArgumentNull(processor, "processor");
            m_operator  = op;
            m_modificationCommandTree = EntityUtil.CheckArgumentNull(tree, "commandTree");
            m_outputIdentifiers       = outputIdentifiers; // may be null (not all commands have output identifiers)

            // initialize identifier information (supports lateral propagation of server gen values)
            if (ModificationOperator.Insert == op || ModificationOperator.Update == op)
            {
                const int capacity = 2; // "average" number of identifiers per row
                m_inputIdentifiers = new List <KeyValuePair <int, DbSetClause> >(capacity);

                foreach (KeyValuePair <EdmMember, PropagatorResult> member in
                         Helper.PairEnumerations(TypeHelpers.GetAllStructuralMembers(this.CurrentValues.StructuralType),
                                                 this.CurrentValues.GetMemberValues()))
                {
                    DbSetClause setter;
                    int         identifier = member.Value.Identifier;

                    if (PropagatorResult.NullIdentifier != identifier &&
                        TryGetSetterExpression(tree, member.Key, op, out setter)) // can find corresponding setter
                    {
                        foreach (int principal in translator.KeyManager.GetPrincipals(identifier))
                        {
                            m_inputIdentifiers.Add(new KeyValuePair <int, DbSetClause>(principal, setter));
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Builds an update command.
        /// </summary>
        /// <param name="oldRow">Old value of the row being updated.</param>
        /// <param name="newRow">New value for the row being updated.</param>
        /// <param name="processor">Context for the table containing row.</param>
        /// <returns>Update command.</returns>
        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)
            var rowMustBeTouched = false;

            var target = GetTarget(processor);

            // Create set clauses and returning parameter
            Dictionary<int, string> outputIdentifiers;
            DbExpression returning;
            var setClauses = new List<DbModificationClause>();
            foreach (var clause in BuildSetClauses(
                target, newRow, oldRow, processor, /* insertMode */ false, out outputIdentifiers, out returning,
                ref rowMustBeTouched))
            {
                setClauses.Add(clause);
            }

            // Construct predicate identifying the row to modify
            var predicate = BuildPredicate(target, oldRow, newRow, processor, ref rowMustBeTouched);

            if (0 == setClauses.Count)
            {
                if (rowMustBeTouched)
                {
                    var stateEntries = new List<IEntityStateEntry>();
                    stateEntries.AddRange(
                        SourceInterpreter.GetAllStateEntries(
                            oldRow, m_translator, processor.Table));
                    stateEntries.AddRange(
                        SourceInterpreter.GetAllStateEntries(
                            newRow, m_translator, processor.Table));
                    if (stateEntries.All(it => (it.State == EntityState.Unchanged)))
                    {
                        rowMustBeTouched = false;
                    }
                }

                // Determine if there is nothing to do (i.e., no values to set, 
                // no computed columns, and no concurrency validation required)
                if (!rowMustBeTouched)
                {
                    return null;
                }
            }

            // Initialize DML command tree
            var commandTree =
                new DbUpdateCommandTree(
                    m_translator.MetadataWorkspace, DataSpace.SSpace, target, predicate, setClauses.AsReadOnly(), returning);

            // Create command
            UpdateCommand command = new DynamicUpdateCommand(
                processor, m_translator, ModificationOperator.Update, oldRow, newRow, commandTree, outputIdentifiers);

            return command;
        }
Esempio n. 6
0
        /// <summary>
        /// Builds an update command.
        /// </summary>
        /// <param name="oldRow">Old value of the row being updated.</param>
        /// <param name="newRow">New value for the row being updated.</param>
        /// <param name="processor">Context for the table containing row.</param>
        /// <returns>Update command.</returns>
        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;

            DbExpressionBinding target = GetTarget(processor);

            // Create set clauses and returning parameter
            Dictionary <int, string>    outputIdentifiers;
            DbExpression                returning;
            List <DbModificationClause> setClauses = new List <DbModificationClause>();

            foreach (DbModificationClause clause in BuildSetClauses(
                         target, newRow, oldRow, processor, /* insertMode */ false, out outputIdentifiers, out returning,
                         ref rowMustBeTouched))
            {
                setClauses.Add(clause);
            }

            // Construct predicate identifying the row to modify
            DbExpression predicate = BuildPredicate(target, oldRow, newRow, processor, ref rowMustBeTouched);

            if (0 == setClauses.Count)
            {
                if (rowMustBeTouched)
                {
                    List <IEntityStateEntry> stateEntries = new List <IEntityStateEntry>();
                    stateEntries.AddRange(SourceInterpreter.GetAllStateEntries(
                                              oldRow, m_translator, processor.Table));
                    stateEntries.AddRange(SourceInterpreter.GetAllStateEntries(
                                              newRow, m_translator, processor.Table));
                    if (stateEntries.All(it => (it.State == EntityState.Unchanged)))
                    {
                        rowMustBeTouched = false;
                    }
                }

                // Determine if there is nothing to do (i.e., no values to set,
                // no computed columns, and no concurrency validation required)
                if (!rowMustBeTouched)
                {
                    return(null);
                }
            }

            // Initialize DML command tree
            DbUpdateCommandTree commandTree =
                new DbUpdateCommandTree(m_translator.MetadataWorkspace, DataSpace.SSpace, target, predicate, setClauses.AsReadOnly(), returning);

            // Create command
            UpdateCommand command = new DynamicUpdateCommand(processor, m_translator, ModificationOperator.Update, oldRow, newRow, commandTree, outputIdentifiers);

            return(command);
        }
Esempio n. 7
0
        /// <summary>
        /// Determines predicate used to identify a row in a table.
        /// </summary>
        /// <remarks>
        /// Columns are included in the list when:
        /// <list>
        /// <item>They are keys for the table</item>
        /// <item>They are concurrency values</item>
        /// </list>
        /// </remarks>
        /// <param name="target">Expression binding representing the table containing the row</param>
        /// <param name="referenceRow">Values for the row being located.</param>
        /// <param name="current">Values being updated (may be null).</param>
        /// <param name="processor">Context for the table containing the row.</param>
        /// <param name="rowMustBeTouched">Output parameter indicating whether a row must be touched
        /// (whether it's being modified or not) because it contains a concurrency value</param>
        /// <returns>Column/value pairs.</returns>
        private DbExpression BuildPredicate(DbExpressionBinding target, PropagatorResult referenceRow, PropagatorResult current,
                                            TableChangeProcessor processor, ref bool rowMustBeTouched)
        {
            Dictionary <EdmProperty, PropagatorResult> whereClauses = new Dictionary <EdmProperty, PropagatorResult>();

            // add all concurrency tokens (note that keys are always concurrency tokens as well)
            int propertyOrdinal = 0;

            foreach (EdmProperty member in processor.Table.ElementType.Properties)
            {
                // 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 <EdmProperty, PropagatorResult> clause in whereClauses)
            {
                DbExpression clauseExpression = GenerateEqualityExpression(target, clause.Key, clause.Value);
                if (null == predicate)
                {
                    predicate = clauseExpression;
                }
                else
                {
                    predicate = predicate.And(clauseExpression);
                }
            }

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

            return(predicate);
        }
        /// <summary>
        /// Builds a delete command. 
        /// </summary>
        /// <param name="oldRow">Value of the row being deleted.</param>
        /// <param name="processor">Context for the table containing row.</param>
        /// <returns>Delete command.</returns>
        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
            DbExpressionBinding target = GetTarget(processor);

            // Create delete predicate
            DbExpression predicate = BuildPredicate(target, oldRow, null, processor, ref rowMustBeTouched);
            DbDeleteCommandTree commandTree = new DbDeleteCommandTree(m_translator.MetadataWorkspace, DataSpace.SSpace, target, predicate);

            // Set command
            // Initialize delete command
            UpdateCommand command = new DynamicUpdateCommand(processor, m_translator, ModificationOperator.Delete, oldRow, null, commandTree, null);

            return command;
        }
Esempio n. 9
0
        /// <summary>
        /// Builds a delete command.
        /// </summary>
        /// <param name="oldRow">Value of the row being deleted.</param>
        /// <param name="processor">Context for the table containing row.</param>
        /// <returns>Delete command.</returns>
        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
            DbExpressionBinding target = GetTarget(processor);

            // Create delete predicate
            DbExpression        predicate   = BuildPredicate(target, oldRow, null, processor, ref rowMustBeTouched);
            DbDeleteCommandTree commandTree = new DbDeleteCommandTree(m_translator.MetadataWorkspace, DataSpace.SSpace, target, predicate);

            // Set command
            // Initialize delete command
            UpdateCommand command = new DynamicUpdateCommand(processor, m_translator, ModificationOperator.Delete, oldRow, null, commandTree, null);

            return(command);
        }
        internal DynamicUpdateCommand(
            TableChangeProcessor processor, UpdateTranslator translator,
            ModificationOperator modificationOperator, PropagatorResult originalValues, PropagatorResult currentValues,
            DbModificationCommandTree tree, Dictionary<int, string> outputIdentifiers)
            : base(translator, originalValues, currentValues)
        {
            //Contract.Requires(processor != null);
            //Contract.Requires(translator != null);
            //Contract.Requires(tree != null);

            _processor = processor;
            _operator = modificationOperator;
            _modificationCommandTree = tree;
            _outputIdentifiers = outputIdentifiers; // may be null (not all commands have output identifiers)

            // initialize identifier information (supports lateral propagation of server gen values)
            if (ModificationOperator.Insert == modificationOperator
                || ModificationOperator.Update == modificationOperator)
            {
                const int capacity = 2; // "average" number of identifiers per row
                _inputIdentifiers = new List<KeyValuePair<int, DbSetClause>>(capacity);

                foreach (var member in
                    Helper.PairEnumerations(
                        TypeHelpers.GetAllStructuralMembers(CurrentValues.StructuralType),
                        CurrentValues.GetMemberValues()))
                {
                    DbSetClause setter;
                    var identifier = member.Value.Identifier;

                    if (PropagatorResult.NullIdentifier != identifier
                        &&
                        TryGetSetterExpression(tree, member.Key, modificationOperator, out setter)) // can find corresponding setter
                    {
                        foreach (var principal in translator.KeyManager.GetPrincipals(identifier))
                        {
                            _inputIdentifiers.Add(new KeyValuePair<int, DbSetClause>(principal, setter));
                        }
                    }
                }
            }
        }
        // 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 DbExpressionBinding GetTarget(TableChangeProcessor processor)
        {
            Debug.Assert(null != processor);

            // use a fixed var name since the command trees all have exactly one binding
            return processor.Table.Scan().BindAs(s_targetVarName);
        }
        /// <summary>
        /// Determines predicate used to identify a row in a table.
        /// </summary>
        /// <remarks>
        /// Columns are included in the list when:
        /// <list>
        /// <item>They are keys for the table</item>
        /// <item>They are concurrency values</item>
        /// </list>
        /// </remarks>
        /// <param name="target">Expression binding representing the table containing the row</param>
        /// <param name="referenceRow">Values for the row being located.</param>
        /// <param name="current">Values being updated (may be null).</param>
        /// <param name="processor">Context for the table containing the row.</param>
        /// <param name="rowMustBeTouched">Output parameter indicating whether a row must be touched
        /// (whether it's being modified or not) because it contains a concurrency value</param>
        /// <returns>Column/value pairs.</returns>
        private DbExpression BuildPredicate(DbExpressionBinding target, PropagatorResult referenceRow, PropagatorResult current,
            TableChangeProcessor processor, ref bool rowMustBeTouched)
        {
            Dictionary<EdmProperty, PropagatorResult> whereClauses = new Dictionary<EdmProperty, PropagatorResult>();

            // add all concurrency tokens (note that keys are always concurrency tokens as well)
            int propertyOrdinal = 0;
            foreach (EdmProperty member in processor.Table.ElementType.Properties)
            {
                // 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<EdmProperty, PropagatorResult> clause in whereClauses)
            {
                DbExpression clauseExpression = GenerateEqualityExpression(target, clause.Key, clause.Value);
                if (null == predicate) { predicate = clauseExpression; }
                else { predicate = predicate.And(clauseExpression); }
            }

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

            return predicate;
        }
        /// <summary>
        /// Determines column/value used to set values for a row.
        /// </summary>
        /// <remarks>
        /// The following columns are not included in the result:
        /// <list>
        /// <item>Keys in non-insert operations (keys are only set for inserts).</item>
        /// <item>Values flagged 'preserve' (these are values the propagator claims are untouched).</item>
        /// <item>Server generated values.</item>
        /// </list>
        /// </remarks>
        /// <param name="target">Expression binding representing the table.</param>
        /// <param name="row">Row containing values to set.</param>
        /// <param name="processor">Context for table.</param>
        /// <param name="insertMode">Determines whether key columns and 'preserve' columns are 
        /// omitted from the list.</param>
        /// <param name="outputIdentifiers">Dictionary listing server generated identifiers.</param>
        /// <param name="returning">DbExpression describing result projection for server generated values.</param>
        /// <param name="rowMustBeTouched">Indicates whether the row must be touched 
        /// because it produces a value (e.g. computed)</param>
        /// <returns>Column value pairs.</returns>
        private IEnumerable<DbModificationClause> BuildSetClauses(DbExpressionBinding target, PropagatorResult row,
            PropagatorResult originalRow, TableChangeProcessor processor, bool insertMode, out Dictionary<int, string> outputIdentifiers, out DbExpression returning,
            ref bool rowMustBeTouched)
        {
            Dictionary<EdmProperty, PropagatorResult> setClauses = new Dictionary<EdmProperty, PropagatorResult>();
            List<KeyValuePair<string, DbExpression>> returningArguments = new List<KeyValuePair<string, DbExpression>>();
            outputIdentifiers = new Dictionary<int, string>();

            // 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 = target.Variable.Property(property);
                    returningArguments.Add(new KeyValuePair<string, DbExpression>(property.Name, propertyExpression));

                    // check if this is a server generated identifier
                    int 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 the error only if the value actually changed
                    Debug.Assert(originalRow != null, "Updated records should have a original row");
                    PropagatorResult originalPropertyResult = originalRow.GetMemberValue(propertyOrdinal);
                    Debug.Assert(originalPropertyResult.IsSimple, "Server Gen property that is not primitive?");
                    Debug.Assert(propertyResult.IsSimple, "Server Gen property that is not primitive?");

                    if (!ByValueEqualityComparer.Default.Equals(originalPropertyResult.GetSimpleValue(), propertyResult.GetSimpleValue()))
                    {
                        throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_ModifyingIdentityColumn(
                            XmlConstants.Identity,
                            property.Name,
                            property.DeclaringType.FullName));
                    }
                    else
                    {
                        omitFromSetList = true;
                    }
                }

                if (!omitFromSetList) { setClauses.Add(property, propertyResult); }
            }

            // Construct returning projection
            if (0 < returningArguments.Count)
            {
                returning = DbExpressionBuilder.NewRow(returningArguments);
            }
            else
            {
                returning = null;
            }

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

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

            return result;
        }
        /// <summary>
        /// Builds insert command.
        /// </summary>
        /// <param name="newRow">Row to insert.</param>
        /// <param name="processor">Context for the table we're inserting into.</param>
        /// <returns>Insert command.</returns>
        internal UpdateCommand BuildInsertCommand(PropagatorResult newRow, TableChangeProcessor processor)
        {
            // Bind the insert target
            DbExpressionBinding target = GetTarget(processor);

            // Create set clauses and returning parameter
            Dictionary<int, string> outputIdentifiers;
            DbExpression returning;
            bool rowMustBeTouched = true; // for inserts, the row must always be touched
            List<DbModificationClause> setClauses = new List<DbModificationClause>();
            foreach (DbModificationClause clause in BuildSetClauses(target, newRow, null, processor, /* insertMode */ true, out outputIdentifiers,
                out returning, ref rowMustBeTouched))
            {
                setClauses.Add(clause);
            }

            // Initialize DML command tree
            DbInsertCommandTree commandTree =
                new DbInsertCommandTree(m_translator.MetadataWorkspace, DataSpace.SSpace, target, setClauses.AsReadOnly(), returning);

            // Create command
            UpdateCommand command = new DynamicUpdateCommand(processor, m_translator, ModificationOperator.Insert, null, newRow, commandTree, outputIdentifiers);

            return command;
        }
        /// <summary>
        /// Produce dynamic store commands for this translator's changes.
        /// </summary>
        /// <returns>Database commands in a safe order</returns>
        private IEnumerable<UpdateCommand> ProduceDynamicCommands()
        {
            // Initialize DBCommand update compiler
            UpdateCompiler updateCompiler = new UpdateCompiler(this);
            
            // Determine affected
            Set<EntitySet> tables = new Set<EntitySet>();

            foreach (EntitySetBase extent in GetDynamicModifiedExtents())
            {
                Set<EntitySet> affectedTables = m_viewLoader.GetAffectedTables(extent, m_metadataWorkspace);
                //Since these extents don't have Functions defined for update operations,
                //the affected tables should be provided via MSL.
                //If we dont find any throw an exception
                if (affectedTables.Count == 0)
                {
                    throw EntityUtil.Update(System.Data.Entity.Strings.Update_MappingNotFound(
                        extent.Name), null /*stateEntries*/);
                }

                foreach (EntitySet table in affectedTables)
                {
                    tables.Add(table);
                }
            }

            // Determine changes to apply to each table
            foreach (EntitySet table in tables)
            {
                DbQueryCommandTree umView = m_connection.GetMetadataWorkspace().GetCqtView(table);
                
                // Propagate changes to root of tree (at which point they are S-Space changes)
                ChangeNode changeNode = Propagator.Propagate(this, table, umView);
                
                // Process changes for the table
                TableChangeProcessor change = new TableChangeProcessor(table);
                foreach (UpdateCommand command in change.CompileCommands(changeNode, updateCompiler))
                {
                    yield return command;
                }
            }
        }
Esempio n. 16
0
        /// <summary>
        /// Determines column/value used to set values for a row.
        /// </summary>
        /// <remarks>
        /// The following columns are not included in the result:
        /// <list>
        /// <item>Keys in non-insert operations (keys are only set for inserts).</item>
        /// <item>Values flagged 'preserve' (these are values the propagator claims are untouched).</item>
        /// <item>Server generated values.</item>
        /// </list>
        /// </remarks>
        /// <param name="target">Expression binding representing the table.</param>
        /// <param name="row">Row containing values to set.</param>
        /// <param name="processor">Context for table.</param>
        /// <param name="insertMode">Determines whether key columns and 'preserve' columns are
        /// omitted from the list.</param>
        /// <param name="outputIdentifiers">Dictionary listing server generated identifiers.</param>
        /// <param name="returning">DbExpression describing result projection for server generated values.</param>
        /// <param name="rowMustBeTouched">Indicates whether the row must be touched
        /// because it produces a value (e.g. computed)</param>
        /// <returns>Column value pairs.</returns>
        private IEnumerable <DbModificationClause> BuildSetClauses(DbExpressionBinding target, PropagatorResult row,
                                                                   PropagatorResult originalRow, TableChangeProcessor processor, bool insertMode, out Dictionary <int, string> outputIdentifiers, out DbExpression returning,
                                                                   ref bool rowMustBeTouched)
        {
            Dictionary <EdmProperty, PropagatorResult>  setClauses         = new Dictionary <EdmProperty, PropagatorResult>();
            List <KeyValuePair <string, DbExpression> > returningArguments = new List <KeyValuePair <string, DbExpression> >();

            outputIdentifiers = new Dictionary <int, string>();

            // 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 = target.Variable.Property(property);
                    returningArguments.Add(new KeyValuePair <string, DbExpression>(property.Name, propertyExpression));

                    // check if this is a server generated identifier
                    int 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 the error only if the value actually changed
                    Debug.Assert(originalRow != null, "Updated records should have a original row");
                    PropagatorResult originalPropertyResult = originalRow.GetMemberValue(propertyOrdinal);
                    Debug.Assert(originalPropertyResult.IsSimple, "Server Gen property that is not primitive?");
                    Debug.Assert(propertyResult.IsSimple, "Server Gen property that is not primitive?");

                    if (!ByValueEqualityComparer.Default.Equals(originalPropertyResult.GetSimpleValue(), propertyResult.GetSimpleValue()))
                    {
                        throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_ModifyingIdentityColumn(
                                                              XmlConstants.Identity,
                                                              property.Name,
                                                              property.DeclaringType.FullName));
                    }
                    else
                    {
                        omitFromSetList = true;
                    }
                }

                if (!omitFromSetList)
                {
                    setClauses.Add(property, propertyResult);
                }
            }

            // Construct returning projection
            if (0 < returningArguments.Count)
            {
                returning = DbExpressionBuilder.NewRow(returningArguments);
            }
            else
            {
                returning = null;
            }

            // Construct clauses corresponding to the set clauses
            List <DbModificationClause> result = new List <DbModificationClause>(setClauses.Count);

            foreach (KeyValuePair <EdmProperty, PropagatorResult> setClause in setClauses)
            {
                EdmProperty property = setClause.Key;

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

            return(result);
        }