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 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); }
// 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)); }
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; }
/// <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); }
/// <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; }
/// <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; } } }
/// <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); }