internal ExtractedStateEntry(EntityState state, PropagatorResult original, PropagatorResult current, IEntityStateEntry source) { State = state; Original = original; Current = current; Source = source; }
/// <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> // Finds all markup associated with the given source. // </summary> // <param name="source"> Source expression. Must not be null. </param> // <param name="translator"> Translator containing session information. </param> // <param name="sourceTable"> Table from which the exception was thrown (must not be null). </param> // <returns> Markup. </returns> internal static ReadOnlyCollection<IEntityStateEntry> GetAllStateEntries( PropagatorResult source, UpdateTranslator translator, EntitySet sourceTable) { DebugCheck.NotNull(source); DebugCheck.NotNull(translator); DebugCheck.NotNull(sourceTable); var interpreter = new SourceInterpreter(translator, sourceTable); interpreter.RetrieveResultMarkup(source); return new ReadOnlyCollection<IEntityStateEntry>(interpreter.m_stateEntries); }
// <summary> // Construct a new placeholder with the shape of the given placeholder. Key values are // injected into the resulting place holder and default values are substituted with // either propagator constants or progagator nulls depending on the mode established // by the <paramref name="mode" /> flag. // </summary> // <remarks> // The key is essentially an array of values. The key map indicates that for a particular // placeholder an expression (keyMap.Keys) corresponds to some ordinal in the key array. // </remarks> // <param name="placeholder"> Placeholder to clone </param> // <param name="key"> Key to substitute </param> // <param name="placeholderKey"> Key elements in the placeholder (ordinally aligned with 'key') </param> // <param name="mode"> Mode of operation. </param> // <returns> Cloned placeholder with key values </returns> internal static PropagatorResult Populate( PropagatorResult placeholder, CompositeKey key, CompositeKey placeholderKey, PopulateMode mode) { DebugCheck.NotNull(placeholder); DebugCheck.NotNull(key); DebugCheck.NotNull(placeholderKey); // Figure out which flags to apply to generated elements. var isNull = mode == PopulateMode.NullModified || mode == PopulateMode.NullPreserve; var preserve = mode == PopulateMode.NullPreserve || mode == PopulateMode.Unknown; var flags = PropagatorFlags.NoFlags; if (!isNull) { flags |= PropagatorFlags.Unknown; } // only null values are known if (preserve) { flags |= PropagatorFlags.Preserve; } var result = placeholder.Replace( node => { // See if this is a key element var keyIndex = -1; for (var i = 0; i < placeholderKey.KeyComponents.Length; i++) { if (placeholderKey.KeyComponents[i] == node) { keyIndex = i; break; } } if (keyIndex != -1) { // Key value. return key.KeyComponents[keyIndex]; } else { // for simple entries, just return using the markup context for this // populator var value = isNull ? null : node.GetSimpleValue(); return PropagatorResult.CreateSimpleValue(flags, value); } }); return result; }
private void RetrieveResultMarkup(PropagatorResult source) { DebugCheck.NotNull(source); if (source.Identifier != PropagatorResult.NullIdentifier) { // state entries travel with identifiers. several state entries may be merged // into a single identifier result via joins in the update mapping view do { if (null != source.StateEntry) { m_stateEntries.Add(source.StateEntry); if (source.Identifier != PropagatorResult.NullIdentifier) { // if this is an identifier, it may also be registered with an "owner". // Return the owner as well if the owner is also mapped to this table. PropagatorResult owner; if (m_translator.KeyManager.TryGetIdentifierOwner(source.Identifier, out owner) && null != owner.StateEntry && ExtentInScope(owner.StateEntry.EntitySet)) { m_stateEntries.Add(owner.StateEntry); } // Check if are any referential constraints. If so, the entity key // implies that the dependent relationship instance is also being // handled in this result. foreach (var stateEntry in m_translator.KeyManager.GetDependentStateEntries(source.Identifier)) { m_stateEntries.Add(stateEntry); } } } source = source.Next; } while (null != source); } else if (!source.IsSimple && !source.IsNull) { // walk children foreach (var child in source.GetMemberValues()) { RetrieveResultMarkup(child); } } }
/// <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 var rowMustBeTouched = true; // Initialize DML command tree var target = GetTarget(processor); // Create delete predicate var predicate = BuildPredicate(target, oldRow, null, processor, ref rowMustBeTouched); var 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> // Produce a tuple containing joined rows. // </summary> // <param name="left"> Left row. </param> // <param name="right"> Right row. </param> // <param name="result"> Result change node; used for type information. </param> // <returns> Result of joining the input rows. </returns> private PropagatorResult CreateResultTuple( Tuple <CompositeKey, PropagatorResult> left, Tuple <CompositeKey, PropagatorResult> right, ChangeNode result) { // using ref compare to avoid triggering value based var leftKey = left.Item1; var rightKey = right.Item1; Dictionary <PropagatorResult, PropagatorResult> map = null; if (!ReferenceEquals(null, leftKey) && !ReferenceEquals(null, rightKey) && !ReferenceEquals(leftKey, rightKey)) { // Merge key values from the left and the right (since they're equal, there's a possibility we'll // project values only from the left or the right hand side and lose important context.) var mergedKey = leftKey.Merge(m_parent.m_updateTranslator.KeyManager, rightKey); // create a dictionary so that we can replace key values with merged key values (carrying context // from both sides) map = new Dictionary <PropagatorResult, PropagatorResult>(); for (var i = 0; i < leftKey.KeyComponents.Length; i++) { map[leftKey.KeyComponents[i]] = mergedKey.KeyComponents[i]; map[rightKey.KeyComponents[i]] = mergedKey.KeyComponents[i]; } } var joinRecordValues = new PropagatorResult[2]; joinRecordValues[0] = left.Item2; joinRecordValues[1] = right.Item2; var join = PropagatorResult.CreateStructuralValue(joinRecordValues, (StructuralType)result.ElementType.EdmType, false); // replace with merged key values as appropriate if (null != map) { PropagatorResult replacement; join = join.Replace(original => map.TryGetValue(original, out replacement) ? replacement : original); } return(join); }
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)); } } } } }
internal DynamicUpdateCommand( TableChangeProcessor processor, UpdateTranslator translator, ModificationOperator modificationOperator, PropagatorResult originalValues, PropagatorResult currentValues, DbModificationCommandTree tree, Dictionary <int, string> outputIdentifiers) : base(translator, originalValues, currentValues) { DebugCheck.NotNull(processor); DebugCheck.NotNull(translator); DebugCheck.NotNull(tree); _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)); } } } } }
private void AddReferencedEntities( UpdateTranslator translator, PropagatorResult result, KeyToListMap <EntityKey, UpdateCommand> referencedEntities) { foreach (PropagatorResult memberValue in result.GetMemberValues()) { if (memberValue.IsSimple && memberValue.Identifier != -1 && PropagatorFlags.ForeignKey == (memberValue.PropagatorFlags & PropagatorFlags.ForeignKey)) { foreach (int directReference in translator.KeyManager.GetDirectReferences(memberValue.Identifier)) { PropagatorResult owner; if (translator.KeyManager.TryGetIdentifierOwner(directReference, out owner) && owner.StateEntry != null) { referencedEntities.Add(owner.StateEntry.EntityKey, this); } } } } }
private Dictionary <CompositeKey, PropagatorResult> ProcessKeys( UpdateCompiler compiler, List <PropagatorResult> changes, Set <CompositeKey> keys) { Dictionary <CompositeKey, PropagatorResult> dictionary = new Dictionary <CompositeKey, PropagatorResult>(compiler.m_translator.KeyComparer); foreach (PropagatorResult change in changes) { PropagatorResult row = change; CompositeKey compositeKey = new CompositeKey(this.GetKeyConstants(row)); PropagatorResult other; if (dictionary.TryGetValue(compositeKey, out other)) { this.DiagnoseKeyCollision(compiler, change, compositeKey, other); } dictionary.Add(compositeKey, row); keys.Add(compositeKey); } return(dictionary); }
protected PropagatorResult[] ReplaceValues( Func <PropagatorResult, PropagatorResult> map) { PropagatorResult[] propagatorResultArray = new PropagatorResult[this.m_values.Length]; bool flag = false; for (int index = 0; index < propagatorResultArray.Length; ++index) { PropagatorResult propagatorResult = this.m_values[index].Replace(map); if (!object.ReferenceEquals((object)propagatorResult, (object)this.m_values[index])) { flag = true; } propagatorResultArray[index] = propagatorResult; } if (!flag) { return((PropagatorResult[])null); } return(propagatorResultArray); }
public void Wraps_exceptions() { var updateTranslatorMock = new Mock <UpdateTranslator> { CallBase = true }; var dbException = new Mock <DbException>("Exception message").Object; var updateCommandMock = new Mock <UpdateCommand>( updateTranslatorMock.Object, PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value: 0), PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value: 0)); updateCommandMock.Setup( m => m.Execute( It.IsAny <Dictionary <int, object> >(), It.IsAny <List <KeyValuePair <PropagatorResult, object> > >(), It.IsAny <IDbCommandInterceptor>())) .Returns(() => { throw dbException; }); var objectStateManager = new Mock <ObjectStateManager> { CallBase = true }.Object; var objectStateEntryMock = new Mock <ObjectStateEntry>(objectStateManager, /*entitySet:*/ null, EntityState.Unchanged); updateCommandMock.Setup(m => m.GetStateEntries(It.IsAny <UpdateTranslator>())) .Returns(new[] { objectStateEntryMock.Object }); new List <KeyValuePair <PropagatorResult, object> >(); updateTranslatorMock.Protected().Setup <IEnumerable <UpdateCommand> >("ProduceCommands").Returns( new[] { updateCommandMock.Object }); var exception = Assert.Throws <UpdateException>(() => updateTranslatorMock.Object.Update()); Assert.Equal(Strings.Update_GeneralExecutionException, exception.Message); Assert.Same(dbException, exception.InnerException); Assert.Same(objectStateEntryMock.Object, exception.StateEntries.Single()); }
internal ExtractedStateEntry(UpdateTranslator translator, IEntityStateEntry stateEntry) { DebugCheck.NotNull(translator); DebugCheck.NotNull(stateEntry); State = stateEntry.State; Source = stateEntry; switch (stateEntry.State) { case EntityState.Deleted: Original = translator.RecordConverter.ConvertOriginalValuesToPropagatorResult( stateEntry, ModifiedPropertiesBehavior.AllModified); Current = null; break; case EntityState.Unchanged: Original = translator.RecordConverter.ConvertOriginalValuesToPropagatorResult( stateEntry, ModifiedPropertiesBehavior.NoneModified); Current = translator.RecordConverter.ConvertCurrentValuesToPropagatorResult( stateEntry, ModifiedPropertiesBehavior.NoneModified); break; case EntityState.Modified: Original = translator.RecordConverter.ConvertOriginalValuesToPropagatorResult( stateEntry, ModifiedPropertiesBehavior.SomeModified); Current = translator.RecordConverter.ConvertCurrentValuesToPropagatorResult( stateEntry, ModifiedPropertiesBehavior.SomeModified); break; case EntityState.Added: Original = null; Current = translator.RecordConverter.ConvertCurrentValuesToPropagatorResult( stateEntry, ModifiedPropertiesBehavior.AllModified); break; default: Debug.Assert(false, "Unexpected IEntityStateEntry.State for entity " + stateEntry.State); Original = null; Current = null; break; } }
internal override FunctionUpdateCommand Translate( UpdateTranslator translator, ExtractedStateEntry stateEntry) { ModificationFunctionMapping functionMapping = this.GetFunctionMapping(stateEntry).Item2; EntityKey entityKey = stateEntry.Source.EntityKey; HashSet <IEntityStateEntry> entityStateEntrySet = new HashSet <IEntityStateEntry>() { stateEntry.Source }; IEnumerable <Tuple <AssociationEndMember, IEntityStateEntry> > tuples = functionMapping.CollocatedAssociationSetEnds.Join <AssociationSetEnd, IEntityStateEntry, StructuralType, Tuple <AssociationEndMember, IEntityStateEntry> >(translator.GetRelationships(entityKey), (Func <AssociationSetEnd, StructuralType>)(end => end.CorrespondingAssociationEndMember.DeclaringType), (Func <IEntityStateEntry, StructuralType>)(candidateEntry => (StructuralType)candidateEntry.EntitySet.ElementType), (Func <AssociationSetEnd, IEntityStateEntry, Tuple <AssociationEndMember, IEntityStateEntry> >)((end, candidateEntry) => Tuple.Create <AssociationEndMember, IEntityStateEntry>(end.CorrespondingAssociationEndMember, candidateEntry))); Dictionary <AssociationEndMember, IEntityStateEntry> dictionary1 = new Dictionary <AssociationEndMember, IEntityStateEntry>(); Dictionary <AssociationEndMember, IEntityStateEntry> dictionary2 = new Dictionary <AssociationEndMember, IEntityStateEntry>(); foreach (Tuple <AssociationEndMember, IEntityStateEntry> tuple in tuples) { ModificationFunctionMappingTranslator.EntitySetTranslator.ProcessReferenceCandidate(entityKey, entityStateEntrySet, dictionary1, dictionary2, tuple.Item1, tuple.Item2); } FunctionUpdateCommand command; if (entityStateEntrySet.All <IEntityStateEntry>((Func <IEntityStateEntry, bool>)(e => e.State == EntityState.Unchanged))) { command = (FunctionUpdateCommand)null; } else { command = new FunctionUpdateCommand(functionMapping, translator, new ReadOnlyCollection <IEntityStateEntry>((IList <IEntityStateEntry>)entityStateEntrySet.ToList <IEntityStateEntry>()), stateEntry); ModificationFunctionMappingTranslator.EntitySetTranslator.BindFunctionParameters(translator, stateEntry, functionMapping, command, dictionary1, dictionary2); if (functionMapping.ResultBindings != null) { foreach (ModificationFunctionResultBinding resultBinding in functionMapping.ResultBindings) { PropagatorResult memberValue = stateEntry.Current.GetMemberValue((EdmMember)resultBinding.Property); command.AddResultColumn(translator, resultBinding.ColumnName, memberValue); } } } return(command); }
internal void AddResultColumn( UpdateTranslator translator, string columnName, PropagatorResult result) { if (this.ResultColumns == null) { this.ResultColumns = new List <KeyValuePair <string, PropagatorResult> >(2); } this.ResultColumns.Add(new KeyValuePair <string, PropagatorResult>(columnName, result)); int identifier = result.Identifier; if (-1 == identifier) { return; } if (translator.KeyManager.HasPrincipals(identifier)) { throw new InvalidOperationException(Strings.Update_GeneratedDependent((object)columnName)); } this.AddOutputIdentifier(columnName, identifier); }
private void ValidateReferentialIntegrityGraphAcyclic( int node, byte[] color, KeyManager.LinkedList <int> parent) { color[node] = (byte)2; KeyManager.LinkedList <int> .Add(ref parent, node); foreach (int node1 in KeyManager.LinkedList <int> .Enumerate(this._identifiers[node].References)) { switch (color[node1]) { case 0: this.ValidateReferentialIntegrityGraphAcyclic(node1, color, parent); continue; case 2: List <IEntityStateEntry> source = new List <IEntityStateEntry>(); foreach (int index in KeyManager.LinkedList <int> .Enumerate(parent)) { PropagatorResult owner = this._identifiers[index].Owner; if (owner != null) { source.Add(owner.StateEntry); } if (index == node1) { break; } } throw new UpdateException(Strings.Update_CircularRelationships, (Exception)null, source.Cast <ObjectStateEntry>().Distinct <ObjectStateEntry>()); default: continue; } } color[node] = (byte)1; }
internal UpdateCommand BuildUpdateCommand( PropagatorResult oldRow, PropagatorResult newRow, TableChangeProcessor processor) { bool rowMustBeTouched = false; DbExpressionBinding target = UpdateCompiler.GetTarget(processor); List <DbModificationClause> modificationClauseList = new List <DbModificationClause>(); Dictionary <int, string> outputIdentifiers; DbExpression returning; foreach (DbModificationClause buildSetClause in this.BuildSetClauses(target, newRow, oldRow, processor, false, out outputIdentifiers, out returning, ref rowMustBeTouched)) { modificationClauseList.Add(buildSetClause); } DbExpression predicate = this.BuildPredicate(target, oldRow, newRow, processor, ref rowMustBeTouched); if (modificationClauseList.Count == 0) { if (rowMustBeTouched) { List <IEntityStateEntry> source = new List <IEntityStateEntry>(); source.AddRange((IEnumerable <IEntityStateEntry>)SourceInterpreter.GetAllStateEntries(oldRow, this.m_translator, processor.Table)); source.AddRange((IEnumerable <IEntityStateEntry>)SourceInterpreter.GetAllStateEntries(newRow, this.m_translator, processor.Table)); if (source.All <IEntityStateEntry>((Func <IEntityStateEntry, bool>)(it => it.State == EntityState.Unchanged))) { rowMustBeTouched = false; } } if (!rowMustBeTouched) { return((UpdateCommand)null); } } DbUpdateCommandTree updateCommandTree = new DbUpdateCommandTree(this.m_translator.MetadataWorkspace, DataSpace.SSpace, target, predicate, new ReadOnlyCollection <DbModificationClause>((IList <DbModificationClause>)modificationClauseList), returning); return((UpdateCommand) new DynamicUpdateCommand(processor, this.m_translator, ModificationOperator.Update, oldRow, newRow, (DbModificationCommandTree)updateCommandTree, outputIdentifiers)); }
internal override PropagatorResult Merge( KeyManager keyManager, PropagatorResult other) { PropagatorResult.KeyValue next = other as PropagatorResult.KeyValue; if (next == null) { EntityUtil.InternalError(EntityUtil.InternalErrorCode.UpdatePipelineResultRequestInvalid, 0, (object)"KeyValue.Merge"); } if (this.Identifier != next.Identifier) { if (keyManager.GetPrincipals(next.Identifier).Contains <int>(this.Identifier)) { return((PropagatorResult)this.ReplicateResultWithNewNext(next)); } return((PropagatorResult)next.ReplicateResultWithNewNext(this)); } if (this.m_stateEntry == null || this.m_stateEntry.IsRelationship) { return((PropagatorResult)next.ReplicateResultWithNewNext(this)); } return((PropagatorResult)this.ReplicateResultWithNewNext(next)); }
internal override PropagatorResult Merge(KeyManager keyManager, PropagatorResult other) { var otherKey = other as KeyValue; if (null == otherKey) { EntityUtil.InternalError(EntityUtil.InternalErrorCode.UpdatePipelineResultRequestInvalid, 0, "KeyValue.Merge"); } // Determine which key (this or otherKey) is first in the chain. Principal keys take // precedence over dependent keys and entities take precedence over relationships. if (Identifier != otherKey.Identifier) { // Find principal (if any) if (keyManager.GetPrincipals(otherKey.Identifier).Contains(Identifier)) { return(ReplicateResultWithNewNext(otherKey)); } else { return(otherKey.ReplicateResultWithNewNext(this)); } } else { // Entity takes precedence of relationship if (null == m_stateEntry || m_stateEntry.IsRelationship) { return(otherKey.ReplicateResultWithNewNext(this)); } else { return(ReplicateResultWithNewNext(otherKey)); } } }
public void Execute_calls_interceptor_when_there_is_no_reader_and_cancels_execution() { var mockUpdateTranslator = new Mock <UpdateTranslator>(MockBehavior.Strict); mockUpdateTranslator.Setup(m => m.CommandTimeout).Returns(43); var entityConnection = new Mock <EntityConnection>().Object; mockUpdateTranslator.Setup(m => m.Connection).Returns(entityConnection); var mockDbModificationCommandTree = new Mock <DbModificationCommandTree>(); var mockDynamicUpdateCommand = new Mock <DynamicUpdateCommand>( new Mock <TableChangeProcessor>().Object, mockUpdateTranslator.Object, ModificationOperator.Delete, PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value: 0), PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value: 0), mockDbModificationCommandTree.Object, /*outputIdentifiers*/ null) { CallBase = true }; var mockDbCommand = new Mock <DbCommand>(); var identifierValues = new Dictionary <int, object>(); mockDynamicUpdateCommand.Protected().Setup <DbCommand>("CreateCommand", identifierValues).Returns(mockDbCommand.Object); var generatedValues = new List <KeyValuePair <PropagatorResult, object> >(); var mockCommandInterceptor = new Mock <IDbCommandInterceptor>(); var rowsAffectedResult = mockDynamicUpdateCommand.Object.Execute( identifierValues, generatedValues, mockCommandInterceptor.Object); Assert.Equal(1, rowsAffectedResult); mockCommandInterceptor.Verify(i => i.Intercept(mockDbCommand.Object)); mockDbCommand.Verify(m => m.ExecuteNonQuery(), Times.Never()); }
private void RetrieveResultMarkup(PropagatorResult source) { if (source.Identifier != -1) { do { if (source.StateEntry != null) { this.m_stateEntries.Add(source.StateEntry); if (source.Identifier != -1) { PropagatorResult owner; if (this.m_translator.KeyManager.TryGetIdentifierOwner(source.Identifier, out owner) && owner.StateEntry != null && this.ExtentInScope(owner.StateEntry.EntitySet)) { this.m_stateEntries.Add(owner.StateEntry); } foreach (IEntityStateEntry dependentStateEntry in this.m_translator.KeyManager.GetDependentStateEntries(source.Identifier)) { this.m_stateEntries.Add(dependentStateEntry); } } } source = source.Next; }while (source != null); } else { if (source.IsSimple || source.IsNull) { return; } foreach (PropagatorResult memberValue in source.GetMemberValues()) { this.RetrieveResultMarkup(memberValue); } } }
// <summary> // Constructor // </summary> // <param name="metadata"> Sets Metadata </param> // <param name="record"> Record containing key value </param> // <param name="isTarget"> Indicates whether the source or target end of the constraint is being pulled </param> // <param name="isInsert"> Indicates whether this is an insert dependency or a delete dependency </param> private ForeignKeyValue( ReferentialConstraint metadata, PropagatorResult record, bool isTarget, bool isInsert) { Metadata = metadata; // construct key IList <EdmProperty> keyProperties = isTarget ? metadata.FromProperties : metadata.ToProperties; var keyValues = new PropagatorResult[keyProperties.Count]; var hasNullMember = false; for (var i = 0; i < keyValues.Length; i++) { keyValues[i] = record.GetMemberValue(keyProperties[i]); if (keyValues[i].IsNull) { hasNullMember = true; break; } } if (hasNullMember) { // set key to null to indicate that it is not behaving as a key // (in SQL, keys with null parts do not participate in constraints) Key = null; } else { Key = new CompositeKey(keyValues); } IsInsert = isInsert; }
private ForeignKeyValue( ReferentialConstraint metadata, PropagatorResult record, bool isTarget, bool isInsert) { this.Metadata = metadata; IList <EdmProperty> edmPropertyList = isTarget ? (IList <EdmProperty>)metadata.FromProperties : (IList <EdmProperty>)metadata.ToProperties; PropagatorResult[] constants = new PropagatorResult[edmPropertyList.Count]; bool flag = false; for (int index = 0; index < constants.Length; ++index) { constants[index] = record.GetMemberValue((EdmMember)edmPropertyList[index]); if (constants[index].IsNull) { flag = true; break; } } this.Key = !flag ? new CompositeKey(constants) : (CompositeKey)null; this.IsInsert = isInsert; }
private void AddReferencedEntities( UpdateTranslator translator, PropagatorResult result, KeyToListMap <EntityKey, UpdateCommand> referencedEntities) { foreach (var property in result.GetMemberValues()) { if (property.IsSimple && property.Identifier != PropagatorResult.NullIdentifier && (PropagatorFlags.ForeignKey == (property.PropagatorFlags & PropagatorFlags.ForeignKey))) { foreach (var principal in translator.KeyManager.GetDirectReferences(property.Identifier)) { PropagatorResult owner; if (translator.KeyManager.TryGetIdentifierOwner(principal, out owner) && null != owner.StateEntry) { Debug.Assert(!owner.StateEntry.IsRelationship, "owner must not be a relationship"); referencedEntities.Add(owner.StateEntry.EntityKey, this); } } } } }
/// <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) { var setClauses = new Dictionary <EdmProperty, PropagatorResult>(); var returningArguments = new List <KeyValuePair <string, DbExpression> >(); outputIdentifiers = new Dictionary <int, string>(); // Determine which flags indicate a property should be omitted from the set list. var omitMask = insertMode ? PropagatorFlags.NoFlags : PropagatorFlags.Preserve | PropagatorFlags.Unknown; for (var propertyOrdinal = 0; propertyOrdinal < processor.Table.ElementType.Properties.Count; propertyOrdinal++) { var property = processor.Table.ElementType.Properties[propertyOrdinal]; // Type members and result values are ordinally aligned var propertyResult = row.GetMemberValue(propertyOrdinal); if (PropagatorResult.NullIdentifier != propertyResult.Identifier) { // retrieve principal value propertyResult = propertyResult.ReplicateResultWithNewValue( m_translator.KeyManager.GetPrincipalValue(propertyResult)); } var omitFromSetList = false; Debug.Assert(propertyResult.IsSimple); // Determine if this is a key value var isKey = false; for (var i = 0; i < processor.KeyOrdinals.Length; i++) { if (processor.KeyOrdinals[i] == propertyOrdinal) { isKey = true; break; } } // check if this value should be omitted var 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 var genPattern = MetadataHelper.GetStoreGeneratedPattern(property); var isServerGen = genPattern == StoreGeneratedPattern.Computed || (insertMode && genPattern == StoreGeneratedPattern.Identity); if (isServerGen) { var propertyExpression = target.Variable.Property(property); returningArguments.Add(new KeyValuePair <string, DbExpression>(property.Name, propertyExpression)); // check if this is a server generated identifier var identifier = propertyResult.Identifier; if (PropagatorResult.NullIdentifier != identifier) { if (m_translator.KeyManager.HasPrincipals(identifier)) { throw new InvalidOperationException(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 new NotSupportedException( 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"); var 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 new InvalidOperationException( 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 var result = new List <DbModificationClause>(setClauses.Count); foreach (var setClause in setClauses) { var property = setClause.Key; result.Add( new DbSetClause( GeneratePropertyExpression(target, setClause.Key), GenerateValueExpression(setClause.Key, setClause.Value))); } return(result); }
// extracts key values from row expression private static CompositeKey ExtractKey( PropagatorResult change, ReadOnlyCollection<DbExpression> keySelectors) { DebugCheck.NotNull(change); DebugCheck.NotNull(keySelectors); var keyValues = new PropagatorResult[keySelectors.Count]; for (var i = 0; i < keySelectors.Count; i++) { var constant = Evaluator.Evaluate(keySelectors[i], change); keyValues[i] = constant; } return new CompositeKey(keyValues); }
// Find "sanctioned" default value internal static void GetPropagatorResultForPrimitiveType(PrimitiveType primitiveType, out PropagatorResult result) { object value; if (!TryGetDefaultValue(primitiveType, out value)) { // If none exists, default to lowest common denominator for constants value = default(byte); } // Return a new constant expression flagged as unknown since the value is only there for // show. (Not entirely for show, because null constraints may require a value for a record, // whether that record is a placeholder or not). result = PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value); }
// 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(EdmProperty property, PropagatorResult value) { DebugCheck.NotNull(property); DebugCheck.NotNull(value); Debug.Assert(value.IsSimple); Debug.Assert(Helper.IsPrimitiveType(property.TypeUsage.EdmType), "Properties in SSpace should be primitive."); if (value.IsNull) { return Helper.GetModelTypeUsage(property).Null(); } var principalValue = m_translator.KeyManager.GetPrincipalValue(value); if (Convert.IsDBNull(principalValue)) { // although the result may be marked non-null (because it is an identifier) it is possible // there is no corresponding real value for the property yet return Helper.GetModelTypeUsage(property).Null(); } else { // At this point we have already done any needed type checking and we potentially translated the type // of the property to the SSpace (the property parameter is a property in the SSpace). However the value // is here is a CSpace value. As a result it does not have to match the type of the property in SSpace. // Two cases here are: // - the type in CSpace does not exactly match the type in the SSpace (but is promotable) // - the type in CSpace is enum type and in this case it never matches the type in SSpace where enum type // does not exist // Since the types have already been checked it is safe just to convert the value from CSpace to the type // from SSpace. Debug.Assert(Nullable.GetUnderlyingType(principalValue.GetType()) == null, "Unexpected nullable type."); var propertyType = Helper.GetModelTypeUsage(property); var principalType = principalValue.GetType(); if (principalType.IsEnum) { principalValue = Convert.ChangeType(principalValue, principalType.GetEnumUnderlyingType(), CultureInfo.InvariantCulture); } var columnClrEquivalentType = ((PrimitiveType)propertyType.EdmType).ClrEquivalentType; if (principalType != columnClrEquivalentType) { principalValue = Convert.ChangeType(principalValue, columnClrEquivalentType, CultureInfo.InvariantCulture); } return propertyType.Constant(principalValue); } }
private void DiagnoseKeyCollision(UpdateCompiler compiler, PropagatorResult change, CompositeKey key, PropagatorResult other) { var keyManager = compiler.m_translator.KeyManager; var otherKey = new CompositeKey(GetKeyConstants(other)); // determine if the conflict is due to shared principal key values var sharedPrincipal = true; for (var i = 0; sharedPrincipal && i < key.KeyComponents.Length; i++) { var identifier1 = key.KeyComponents[i].Identifier; var identifier2 = otherKey.KeyComponents[i].Identifier; if (!keyManager.GetPrincipals(identifier1).Intersect(keyManager.GetPrincipals(identifier2)).Any()) { sharedPrincipal = false; } } if (sharedPrincipal) { // if the duplication is due to shared principals, there is a duplicate key exception var stateEntries = SourceInterpreter.GetAllStateEntries(change, compiler.m_translator, m_table) .Concat(SourceInterpreter.GetAllStateEntries(other, compiler.m_translator, m_table)); throw new UpdateException(Strings.Update_DuplicateKeys, null, stateEntries.Cast<ObjectStateEntry>().Distinct()); } else { // if there are no shared principals, it implies that common dependents are the problem HashSet<IEntityStateEntry> commonDependents = null; foreach (var keyValue in key.KeyComponents.Concat(otherKey.KeyComponents)) { var dependents = new HashSet<IEntityStateEntry>(); foreach (var dependentId in keyManager.GetDependents(keyValue.Identifier)) { PropagatorResult dependentResult; if (keyManager.TryGetIdentifierOwner(dependentId, out dependentResult) && null != dependentResult.StateEntry) { dependents.Add(dependentResult.StateEntry); } } if (null == commonDependents) { commonDependents = new HashSet<IEntityStateEntry>(dependents); } else { commonDependents.IntersectWith(dependents); } } // to ensure the exception shape is consistent with constraint violations discovered while processing // commands (a more conventional scenario in which different tables are contributing principal values) // wrap a DataConstraintException in an UpdateException throw new UpdateException( Strings.Update_GeneralExecutionException, new ConstraintException(Strings.Update_ReferentialConstraintIntegrityViolation), commonDependents.Cast<ObjectStateEntry>().Distinct()); } }
/// <summary> /// Given default values for children members, produces a new default expression for the requested (parent) member. /// </summary> /// <param name="node"> Parent member </param> /// <returns> Default value for parent member </returns> internal PropagatorResult Visit(EdmMember node) { PropagatorResult result; var nodeType = Helper.GetModelTypeUsage(node); if (Helper.IsScalarType(nodeType.EdmType)) { GetPropagatorResultForPrimitiveType(Helper.AsPrimitive(nodeType.EdmType), out result); } else { // Construct a new 'complex type' (really any structural type) member. var structuralType = (StructuralType)nodeType.EdmType; var members = TypeHelpers.GetAllStructuralMembers(structuralType); var args = new PropagatorResult[members.Count]; for (var ordinal = 0; ordinal < members.Count; ordinal++) // foreach (EdmMember member in members) { args[ordinal] = Visit(members[ordinal]); } result = PropagatorResult.CreateStructuralValue(args, structuralType, false); } return result; }
/// <summary> /// A result is merged with another when it is merged as part of an equi-join. /// </summary> /// <remarks> /// In theory, this should only ever be called on two keys (since we only join on /// keys). We throw in the base implementation, and override in KeyResult. By convention /// the principal key is always the first result in the chain (in case of an RIC). In /// addition, entity entries always appear before relationship entries. /// </remarks> /// <param name="other">Result to merge with.</param> /// <returns>Merged result.</returns> internal virtual PropagatorResult Merge(KeyManager keyManager, PropagatorResult other) { throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.UpdatePipelineResultRequestInvalid, 0, "PropagatorResult.Merge"); }
internal override PropagatorResult Merge(KeyManager keyManager, PropagatorResult other) { var otherKey = other as KeyValue; if (null == otherKey) { EntityUtil.InternalError(EntityUtil.InternalErrorCode.UpdatePipelineResultRequestInvalid, 0, "KeyValue.Merge"); } // Determine which key (this or otherKey) is first in the chain. Principal keys take // precedence over dependent keys and entities take precedence over relationships. if (Identifier != otherKey.Identifier) { // Find principal (if any) if (keyManager.GetPrincipals(otherKey.Identifier).Contains(Identifier)) { return ReplicateResultWithNewNext(otherKey); } else { return otherKey.ReplicateResultWithNewNext(this); } } else { // Entity takes precedence of relationship if (null == m_stateEntry || m_stateEntry.IsRelationship) { return otherKey.ReplicateResultWithNewNext(this); } else { return ReplicateResultWithNewNext(otherKey); } } }
public void Returns_rows_affected_when_there_is_a_reader() { var mockPrimitiveType = new Mock <PrimitiveType>(); mockPrimitiveType.Setup(m => m.BuiltInTypeKind).Returns(BuiltInTypeKind.PrimitiveType); mockPrimitiveType.Setup(m => m.PrimitiveTypeKind).Returns(PrimitiveTypeKind.Int32); mockPrimitiveType.Setup(m => m.DataSpace).Returns(DataSpace.CSpace); var memberName = "property"; var edmProperty = new EdmProperty(memberName, TypeUsage.Create(mockPrimitiveType.Object)); var entityType = new EntityType("", "", DataSpace.CSpace, Enumerable.Empty <string>(), new[] { edmProperty }); entityType.SetReadOnly(); var mockUpdateTranslator = new Mock <UpdateTranslator>(MockBehavior.Strict); mockUpdateTranslator.Setup(m => m.CommandTimeout).Returns(() => null); var entityConnection = new Mock <EntityConnection>().Object; mockUpdateTranslator.Setup(m => m.Connection).Returns(entityConnection); var mockDbModificationCommandTree = new Mock <DbModificationCommandTree>(); mockDbModificationCommandTree.SetupGet(m => m.HasReader).Returns(true); var mockDynamicUpdateCommand = new Mock <DynamicUpdateCommand>( new Mock <TableChangeProcessor>().Object, mockUpdateTranslator.Object, ModificationOperator.Delete, PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value: 0), PropagatorResult.CreateStructuralValue( new[] { PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value: 0) }, entityType, isModified: false), mockDbModificationCommandTree.Object, /*outputIdentifiers*/ null) { CallBase = true }; var mockDbCommand = new Mock <DbCommand>(); var dbValue = 66; var mockDbDataReader = new Mock <DbDataReader>(); mockDbDataReader.Setup(m => m.GetFieldValueAsync <object>(It.IsAny <int>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult <object>(dbValue)); var rowsToRead = 2; mockDbDataReader.Setup(m => m.ReadAsync(It.IsAny <CancellationToken>())).Returns( () => { rowsToRead--; return(Task.FromResult(rowsToRead > 0)); }); mockDbDataReader.Setup(m => m.FieldCount).Returns(1); mockDbDataReader.Setup(m => m.GetName(0)).Returns(memberName); mockDbDataReader.Setup(m => m.NextResultAsync(It.IsAny <CancellationToken>())).Returns(Task.FromResult(false)); mockDbCommand.Protected() .Setup <Task <DbDataReader> >("ExecuteDbDataReaderAsync", CommandBehavior.SequentialAccess, It.IsAny <CancellationToken>()) .Returns(Task.FromResult(mockDbDataReader.Object)); var identifierValues = new Dictionary <int, object>(); mockDynamicUpdateCommand.Protected().Setup <DbCommand>("CreateCommand", identifierValues).Returns(mockDbCommand.Object); var generatedValues = new List <KeyValuePair <PropagatorResult, object> >(); var rowsAffectedResult = mockDynamicUpdateCommand.Object.ExecuteAsync(identifierValues, generatedValues, CancellationToken.None).Result; Assert.Equal(1, rowsAffectedResult); Assert.Equal(1, generatedValues.Count); Assert.Equal(dbValue, generatedValues[0].Value); Assert.Equal(0, generatedValues[0].Key.GetSimpleValue()); }
internal override async Task <long> ExecuteAsync( Dictionary <int, object> identifierValues, List <KeyValuePair <PropagatorResult, object> > generatedValues, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); long num; using (DbCommand command = this.CreateCommand(identifierValues)) { EntityConnection connection = this.Translator.Connection; command.Transaction = connection.CurrentTransaction == null ? (DbTransaction)null : connection.CurrentTransaction.StoreTransaction; command.Connection = connection.StoreConnection; if (this.Translator.CommandTimeout.HasValue) { command.CommandTimeout = this.Translator.CommandTimeout.Value; } int rowsAffected; if (this._modificationCommandTree.HasReader) { rowsAffected = 0; using (DbDataReader reader = await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken).WithCurrentCulture <DbDataReader>()) { if (await reader.ReadAsync(cancellationToken).WithCurrentCulture <bool>()) { ++rowsAffected; IBaseList <EdmMember> members = TypeHelpers.GetAllStructuralMembers((EdmType)this.CurrentValues.StructuralType); for (int ordinal = 0; ordinal < reader.FieldCount; ++ordinal) { string columnName = reader.GetName(ordinal); EdmMember member = members[columnName]; object value; if (Helper.IsSpatialType(member.TypeUsage)) { if (!await reader.IsDBNullAsync(ordinal, cancellationToken).WithCurrentCulture <bool>()) { value = await SpatialHelpers.GetSpatialValueAsync(this.Translator.MetadataWorkspace, reader, member.TypeUsage, ordinal, cancellationToken).WithCurrentCulture <object>(); goto label_14; } } value = await reader.GetFieldValueAsync <object>(ordinal, cancellationToken).WithCurrentCulture <object>(); label_14: int columnOrdinal = members.IndexOf(member); PropagatorResult result = this.CurrentValues.GetMemberValue(columnOrdinal); generatedValues.Add(new KeyValuePair <PropagatorResult, object>(result, value)); int identifier = result.Identifier; if (-1 != identifier) { identifierValues.Add(identifier, value); } } } await CommandHelper.ConsumeReaderAsync(reader, cancellationToken).WithCurrentCulture(); } } else { rowsAffected = await command.ExecuteNonQueryAsync(cancellationToken).WithCurrentCulture <int>(); } num = (long)rowsAffected; } return(num); }
// Adds a result column binding from a column name (from the result set for the function) to // a propagator result (which contains the context necessary to back-propagate the result). // If the result is an identifier, binds the internal void AddResultColumn(UpdateTranslator translator, String columnName, PropagatorResult result) { const int initializeSize = 2; // expect on average less than two result columns per command if (null == ResultColumns) { ResultColumns = new List<KeyValuePair<string, PropagatorResult>>(initializeSize); } ResultColumns.Add(new KeyValuePair<string, PropagatorResult>(columnName, result)); var identifier = result.Identifier; if (PropagatorResult.NullIdentifier != identifier) { if (translator.KeyManager.HasPrincipals(identifier)) { throw new InvalidOperationException(Strings.Update_GeneratedDependent(columnName)); } // register output identifier to enable fix-up and dependency tracking AddOutputIdentifier(columnName, identifier); } }
// Adds and register a DbParameter to the current command. internal void SetParameterValue( PropagatorResult result, StorageModificationFunctionParameterBinding parameterBinding, UpdateTranslator translator) { // retrieve DbParameter var parameter = _dbCommand.Parameters[parameterBinding.Parameter.Name]; var parameterType = parameterBinding.Parameter.TypeUsage; var parameterValue = translator.KeyManager.GetPrincipalValue(result); translator.SetParameterValue(parameter, parameterType, parameterValue); // if the parameter corresponds to an identifier (key component), remember this fact in case // it's important for dependency ordering (e.g., output the identifier before creating it) var identifier = result.Identifier; if (PropagatorResult.NullIdentifier != identifier) { const int initialSize = 2; // expect on average less than two input identifiers per command if (null == _inputIdentifiers) { _inputIdentifiers = new List<KeyValuePair<int, DbParameter>>(initialSize); } foreach (var principal in translator.KeyManager.GetPrincipals(identifier)) { _inputIdentifiers.Add(new KeyValuePair<int, DbParameter>(principal, parameter)); } } }
public void Returns_rows_affected_when_there_are_result_columns() { var mockPrimitiveType = new Mock <PrimitiveType>(); mockPrimitiveType.Setup(m => m.BuiltInTypeKind).Returns(BuiltInTypeKind.PrimitiveType); mockPrimitiveType.Setup(m => m.PrimitiveTypeKind).Returns(PrimitiveTypeKind.Int32); mockPrimitiveType.Setup(m => m.DataSpace).Returns(DataSpace.CSpace); var edmProperty = new EdmProperty("property", TypeUsage.Create(mockPrimitiveType.Object)); var entityType = new EntityType("", "", DataSpace.CSpace, Enumerable.Empty <string>(), new[] { edmProperty }); entityType.SetReadOnly(); var stateEntry = new ExtractedStateEntry( EntityState.Unchanged, PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value: 0), PropagatorResult.CreateStructuralValue( new[] { PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value: 0) }, entityType, isModified: false), new Mock <IEntityStateEntry>().Object); var updateTranslatorMock = new Mock <UpdateTranslator>(); updateTranslatorMock.Setup(m => m.CommandTimeout).Returns(() => null); var entityConnection = new Mock <EntityConnection>().Object; updateTranslatorMock.Setup(m => m.Connection).Returns(entityConnection); updateTranslatorMock.Setup(m => m.InterceptionContext).Returns(new DbInterceptionContext()); var dbCommandMock = new Mock <DbCommand>(); var stateEntries = new ReadOnlyCollection <IEntityStateEntry>(new List <IEntityStateEntry>()); var mockFunctionUpdateCommand = new Mock <FunctionUpdateCommand>( updateTranslatorMock.Object, stateEntries, stateEntry, dbCommandMock.Object) { CallBase = true }; var dbValue = 66; var dbDataReaderMock = new Mock <DbDataReader>(); dbDataReaderMock.Setup(m => m.GetValue(It.IsAny <int>())).Returns(dbValue); var rowsToRead = 2; dbDataReaderMock.Setup(m => m.Read()).Returns( () => { rowsToRead--; return(rowsToRead > 0); }); dbCommandMock.Protected().Setup <DbDataReader>("ExecuteDbDataReader", CommandBehavior.SequentialAccess).Returns( dbDataReaderMock.Object); var timesSetInputIdentifiers = 0; var identifierValues = new Dictionary <int, object>(); mockFunctionUpdateCommand.Setup(m => m.SetInputIdentifiers(It.IsAny <Dictionary <int, object> >())) .Callback <Dictionary <int, object> >( identifierValuesPassed => { timesSetInputIdentifiers++; Assert.Same(identifierValues, identifierValuesPassed); }); var generatedValues = new List <KeyValuePair <PropagatorResult, object> >(); var mockObjectStateManager = new Mock <ObjectStateManager>(); var objectStateEntryMock = new Mock <ObjectStateEntry>(mockObjectStateManager.Object, null, EntityState.Unchanged); var currentValueRecordMock = new Mock <CurrentValueRecord>(objectStateEntryMock.Object); var idColumn = new KeyValuePair <string, PropagatorResult>( "ID", PropagatorResult.CreateServerGenSimpleValue( PropagatorFlags.NoFlags, /*value:*/ 0, currentValueRecordMock.Object, recordOrdinal: 0)); mockFunctionUpdateCommand.Protected().Setup <List <KeyValuePair <string, PropagatorResult> > >("ResultColumns") .Returns((new[] { idColumn }).ToList()); var rowsAffectedResult = mockFunctionUpdateCommand.Object.Execute(identifierValues, generatedValues); Assert.Equal(1, rowsAffectedResult); Assert.Equal(1, timesSetInputIdentifiers); Assert.Equal(1, generatedValues.Count); Assert.Same(idColumn.Value, generatedValues[0].Key); Assert.Equal(dbValue, generatedValues[0].Value); }
// Note that this is called only for association ends. Entities have key values inline. private PropagatorResult CreateEntityKeyResult(IEntityStateEntry stateEntry, EntityKey entityKey) { // get metadata for key var entityType = entityKey.GetEntitySet(m_translator.MetadataWorkspace).ElementType; var keyRowType = entityType.GetKeyRowType(); var keyMetadata = m_translator.GetExtractorMetadata(stateEntry.EntitySet, keyRowType); var keyMemberCount = keyRowType.Properties.Count; var keyValues = new PropagatorResult[keyMemberCount]; for (var ordinal = 0; ordinal < keyRowType.Properties.Count; ordinal++) { EdmMember keyMember = keyRowType.Properties[ordinal]; // retrieve information about this key value var keyMemberInformation = keyMetadata.m_memberMap[ordinal]; var keyIdentifier = m_translator.KeyManager.GetKeyIdentifierForMemberOffset(entityKey, ordinal, keyRowType.Properties.Count); object keyValue = null; if (entityKey.IsTemporary) { // If the EntityKey is temporary, we need to retrieve the appropriate // key value from the entity itself (or in this case, the IEntityStateEntry). var entityEntry = stateEntry.StateManager.GetEntityStateEntry(entityKey); Debug.Assert( entityEntry.State == EntityState.Added, "The corresponding entry for a temp EntityKey should be in the Added State."); keyValue = entityEntry.CurrentValues[keyMember.Name]; } else { // Otherwise, we extract the value from within the EntityKey. keyValue = entityKey.FindValueByName(keyMember.Name); } Debug.Assert(keyValue != null, "keyValue should've been retrieved."); // construct propagator result keyValues[ordinal] = PropagatorResult.CreateKeyValue( keyMemberInformation.Flags, keyValue, stateEntry, keyIdentifier); // see UpdateTranslator.Identifiers for information on key identifiers and ordinals } return PropagatorResult.CreateStructuralValue(keyValues, keyMetadata.m_type, false); }
internal static PropagatorResult CreateStructuralValue(PropagatorResult[] values, StructuralType structuralType, bool isModified) { if (isModified) { return new StructuralValue(values, structuralType); } else { return new UnmodifiedStructuralValue(values, structuralType); } }
/// <summary> /// Specialization of <see cref="CreatePlaceholder" /> for a relationship set extent. /// </summary> /// <param name="associationSet"> </param> /// <returns> </returns> private PropagatorResult CreateAssociationSetPlaceholder(AssociationSet associationSet) { DebugCheck.NotNull(associationSet); var endMetadata = associationSet.ElementType.AssociationEndMembers; var endReferenceValues = new PropagatorResult[endMetadata.Count]; // Create a reference expression for each end in the relationship for (var endOrdinal = 0; endOrdinal < endMetadata.Count; endOrdinal++) { var end = endMetadata[endOrdinal]; var entityType = (EntityType)((RefType)end.TypeUsage.EdmType).ElementType; // Retrieve key values for this end var keyValues = new PropagatorResult[entityType.KeyMembers.Count]; for (var memberOrdinal = 0; memberOrdinal < entityType.KeyMembers.Count; memberOrdinal++) { var keyMember = entityType.KeyMembers[memberOrdinal]; var keyValue = CreateMemberPlaceholder(keyMember); keyValues[memberOrdinal] = keyValue; } var endType = entityType.GetKeyRowType(); var refKeys = PropagatorResult.CreateStructuralValue(keyValues, endType, false); endReferenceValues[endOrdinal] = refKeys; } var result = PropagatorResult.CreateStructuralValue(endReferenceValues, associationSet.ElementType, false); return result; }
internal StructuralValue(PropagatorResult[] values, StructuralType structuralType) { Debug.Assert(null != structuralType); Debug.Assert(null != values); Debug.Assert(values.Length == TypeHelpers.GetAllStructuralMembers(structuralType).Count); m_values = values; m_structuralType = structuralType; }
internal virtual PropagatorResult Merge(KeyManager keyManager, PropagatorResult other) { throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.UpdatePipelineResultRequestInvalid, 0, "PropagatorResult.Merge"); }
protected UpdateCommand(UpdateTranslator translator, PropagatorResult originalValues, PropagatorResult currentValues) { OriginalValues = originalValues; CurrentValues = currentValues; Translator = translator; }
// 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(DbExpressionBinding target, EdmProperty property, PropagatorResult value) { DebugCheck.NotNull(target); DebugCheck.NotNull(property); DebugCheck.NotNull(value); var propertyExpression = GeneratePropertyExpression(target, property); var valueExpression = GenerateValueExpression(property, value); if (valueExpression.ExpressionKind == DbExpressionKind.Null) { return propertyExpression.IsNull(); } return propertyExpression.Equal(valueExpression); }
// Extracts key constants from the given row. private PropagatorResult[] GetKeyConstants(PropagatorResult row) { var keyConstants = new PropagatorResult[m_keyOrdinals.Length]; for (var i = 0; i < m_keyOrdinals.Length; i++) { var constant = row.GetMemberValue(m_keyOrdinals[i]); keyConstants[i] = constant; } return keyConstants; }
/// <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) { var whereClauses = new Dictionary<EdmProperty, PropagatorResult>(); // add all concurrency tokens (note that keys are always concurrency tokens as well) var propertyOrdinal = 0; foreach (var member in processor.Table.ElementType.Properties) { // members and result values are ordinally aligned var expectedValue = referenceRow.GetMemberValue(propertyOrdinal); var 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 (var clause in whereClauses) { var 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; }
protected PropagatorResult[] ReplaceValues(Func<PropagatorResult, PropagatorResult> map) { var newValues = new PropagatorResult[m_values.Length]; var hasChange = false; for (var i = 0; i < newValues.Length; i++) { var newValue = m_values[i].Replace(map); if (!ReferenceEquals(newValue, m_values[i])) { hasChange = true; } newValues[i] = newValue; } return hasChange ? newValues : null; }
// Adds a result column binding from a column name (from the result set for the function) to // a propagator result (which contains the context necessary to back-propagate the result). // If the result is an identifier, binds the internal void AddResultColumn(UpdateTranslator translator, String columnName, PropagatorResult result) { const int initializeSize = 2; // expect on average less than two result columns per command if (null == ResultColumns) { ResultColumns = new List <KeyValuePair <string, PropagatorResult> >(initializeSize); } ResultColumns.Add(new KeyValuePair <string, PropagatorResult>(columnName, result)); var identifier = result.Identifier; if (PropagatorResult.NullIdentifier != identifier) { if (translator.KeyManager.HasPrincipals(identifier)) { throw new InvalidOperationException(Strings.Update_GeneratedDependent(columnName)); } // register output identifier to enable fix-up and dependency tracking AddOutputIdentifier(columnName, identifier); } }
private void DiagnoseKeyCollision(UpdateCompiler compiler, PropagatorResult change, CompositeKey key, PropagatorResult other) { var keyManager = compiler.m_translator.KeyManager; var otherKey = new CompositeKey(GetKeyConstants(other)); // determine if the conflict is due to shared principal key values var sharedPrincipal = true; for (var i = 0; sharedPrincipal && i < key.KeyComponents.Length; i++) { var identifier1 = key.KeyComponents[i].Identifier; var identifier2 = otherKey.KeyComponents[i].Identifier; if (!keyManager.GetPrincipals(identifier1).Intersect(keyManager.GetPrincipals(identifier2)).Any()) { sharedPrincipal = false; } } if (sharedPrincipal) { // if the duplication is due to shared principals, there is a duplicate key exception var stateEntries = SourceInterpreter.GetAllStateEntries(change, compiler.m_translator, m_table) .Concat(SourceInterpreter.GetAllStateEntries(other, compiler.m_translator, m_table)); throw new UpdateException(Strings.Update_DuplicateKeys, null, stateEntries.Cast <ObjectStateEntry>().Distinct()); } else { // if there are no shared principals, it implies that common dependents are the problem HashSet <IEntityStateEntry> commonDependents = null; foreach (var keyValue in key.KeyComponents.Concat(otherKey.KeyComponents)) { var dependents = new HashSet <IEntityStateEntry>(); foreach (var dependentId in keyManager.GetDependents(keyValue.Identifier)) { PropagatorResult dependentResult; if (keyManager.TryGetIdentifierOwner(dependentId, out dependentResult) && null != dependentResult.StateEntry) { dependents.Add(dependentResult.StateEntry); } } if (null == commonDependents) { commonDependents = new HashSet <IEntityStateEntry>(dependents); } else { commonDependents.IntersectWith(dependents); } } // to ensure the exception shape is consistent with constraint violations discovered while processing // commands (a more conventional scenario in which different tables are contributing principal values) // wrap a DataConstraintException in an UpdateException throw new UpdateException( Strings.Update_GeneralExecutionException, new ConstraintException(Strings.Update_ReferentialConstraintIntegrityViolation), commonDependents.Cast <ObjectStateEntry>().Distinct()); } }
internal UnmodifiedStructuralValue(PropagatorResult[] values, StructuralType structuralType) : base(values, structuralType) { }
internal override async Task <long> ExecuteAsync( Dictionary <int, object> identifierValues, List <KeyValuePair <PropagatorResult, object> > generatedValues, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); EntityConnection connection = this.Translator.Connection; this._dbCommand.Transaction = connection.CurrentTransaction == null ? (DbTransaction)null : connection.CurrentTransaction.StoreTransaction; this._dbCommand.Connection = connection.StoreConnection; if (this.Translator.CommandTimeout.HasValue) { this._dbCommand.CommandTimeout = this.Translator.CommandTimeout.Value; } this.SetInputIdentifiers(identifierValues); long rowsAffected; if (this.ResultColumns != null) { rowsAffected = 0L; IBaseList <EdmMember> members = TypeHelpers.GetAllStructuralMembers((EdmType)this.CurrentValues.StructuralType); using (DbDataReader reader = await this._dbCommand.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken).WithCurrentCulture <DbDataReader>()) { if (await reader.ReadAsync(cancellationToken).WithCurrentCulture <bool>()) { ++rowsAffected; foreach (KeyValuePair <int, PropagatorResult> keyValuePair in (IEnumerable <KeyValuePair <int, PropagatorResult> >) this.ResultColumns.Select <KeyValuePair <string, PropagatorResult>, KeyValuePair <int, PropagatorResult> >((Func <KeyValuePair <string, PropagatorResult>, KeyValuePair <int, PropagatorResult> >)(r => new KeyValuePair <int, PropagatorResult>(this.GetColumnOrdinal(this.Translator, reader, r.Key), r.Value))).OrderBy <KeyValuePair <int, PropagatorResult>, int>((Func <KeyValuePair <int, PropagatorResult>, int>)(r => r.Key))) { int columnOrdinal = keyValuePair.Key; TypeUsage columnType = members[keyValuePair.Value.RecordOrdinal].TypeUsage; object value; if (Helper.IsSpatialType(columnType)) { if (!await reader.IsDBNullAsync(columnOrdinal, cancellationToken).WithCurrentCulture <bool>()) { value = await SpatialHelpers.GetSpatialValueAsync(this.Translator.MetadataWorkspace, reader, columnType, columnOrdinal, cancellationToken).WithCurrentCulture <object>(); goto label_14; } } value = await reader.GetFieldValueAsync <object>(columnOrdinal, cancellationToken).WithCurrentCulture <object>(); label_14: PropagatorResult result = keyValuePair.Value; generatedValues.Add(new KeyValuePair <PropagatorResult, object>(result, value)); int identifier = result.Identifier; if (-1 != identifier) { identifierValues.Add(identifier, value); } } } await CommandHelper.ConsumeReaderAsync(reader, cancellationToken).WithCurrentCulture(); } } else { rowsAffected = (long)await this._dbCommand.ExecuteNonQueryAsync(cancellationToken).WithCurrentCulture <int>(); } return(this.GetRowsAffected(rowsAffected, this.Translator)); }
internal bool TryGetIdentifierOwner(int identifier, out PropagatorResult owner) { owner = this._identifiers[identifier].Owner; return(null != owner); }
/// <summary> /// Produce a tuple containing joined rows. /// </summary> /// <param name="left"> Left row. </param> /// <param name="right"> Right row. </param> /// <param name="leftKey"> Key used to join left element. </param> /// <param name="rightKey"> Key used to join right element. </param> /// <param name="result"> Result change node; used for type information. </param> /// <returns> Result of joining the input rows. </returns> private PropagatorResult CreateResultTuple( Tuple<CompositeKey, PropagatorResult> left, Tuple<CompositeKey, PropagatorResult> right, ChangeNode result) { // using ref compare to avoid triggering value based var leftKey = left.Item1; var rightKey = right.Item1; Dictionary<PropagatorResult, PropagatorResult> map = null; if (!ReferenceEquals(null, leftKey) && !ReferenceEquals(null, rightKey) && !ReferenceEquals(leftKey, rightKey)) { // Merge key values from the left and the right (since they're equal, there's a possibility we'll // project values only from the left or the right hand side and lose important context.) var mergedKey = leftKey.Merge(m_parent.m_updateTranslator.KeyManager, rightKey); // create a dictionary so that we can replace key values with merged key values (carrying context // from both sides) map = new Dictionary<PropagatorResult, PropagatorResult>(); for (var i = 0; i < leftKey.KeyComponents.Length; i++) { map[leftKey.KeyComponents[i]] = mergedKey.KeyComponents[i]; map[rightKey.KeyComponents[i]] = mergedKey.KeyComponents[i]; } } var joinRecordValues = new PropagatorResult[2]; joinRecordValues[0] = left.Item2; joinRecordValues[1] = right.Item2; var join = PropagatorResult.CreateStructuralValue(joinRecordValues, (StructuralType)result.ElementType.EdmType, false); // replace with merged key values as appropriate if (null != map) { PropagatorResult replacement; join = join.Replace(original => map.TryGetValue(original, out replacement) ? replacement : original); } return join; }
// 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)); }
// Walks through all parameter bindings in the function mapping and binds the parameters to the // requested properties of the given state entry. private static void BindFunctionParameters( UpdateTranslator translator, ExtractedStateEntry stateEntry, ModificationFunctionMapping functionMapping, FunctionUpdateCommand command, Dictionary <AssociationEndMember, IEntityStateEntry> currentReferenceEnds, Dictionary <AssociationEndMember, IEntityStateEntry> originalReferenceEnds) { // bind all parameters foreach (var parameterBinding in functionMapping.ParameterBindings) { PropagatorResult result; // extract value if (null != parameterBinding.MemberPath.AssociationSetEnd) { // find the relationship entry corresponding to the navigation var endMember = parameterBinding.MemberPath.AssociationSetEnd.CorrespondingAssociationEndMember; IEntityStateEntry relationshipEntry; var hasTarget = parameterBinding.IsCurrent ? currentReferenceEnds.TryGetValue(endMember, out relationshipEntry) : originalReferenceEnds.TryGetValue(endMember, out relationshipEntry); if (!hasTarget) { if (endMember.RelationshipMultiplicity == RelationshipMultiplicity.One) { var entitySetName = stateEntry.Source.EntitySet.Name; var associationSetName = parameterBinding.MemberPath.AssociationSetEnd.ParentAssociationSet.Name; throw new UpdateException( Strings.Update_MissingRequiredRelationshipValue(entitySetName, associationSetName), null, command.GetStateEntries(translator).Cast <ObjectStateEntry>().Distinct()); } else { result = PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, null); } } else { // get the actual value var relationshipResult = parameterBinding.IsCurrent ? translator.RecordConverter.ConvertCurrentValuesToPropagatorResult( relationshipEntry, ModifiedPropertiesBehavior.AllModified) : translator.RecordConverter.ConvertOriginalValuesToPropagatorResult( relationshipEntry, ModifiedPropertiesBehavior.AllModified); var endResult = relationshipResult.GetMemberValue(endMember); var keyProperty = (EdmProperty)parameterBinding.MemberPath.Members[0]; result = endResult.GetMemberValue(keyProperty); } } else { // walk through the member path to find the appropriate propagator results result = parameterBinding.IsCurrent ? stateEntry.Current : stateEntry.Original; for (var i = parameterBinding.MemberPath.Members.Count; i > 0;) { --i; var member = parameterBinding.MemberPath.Members[i]; result = result.GetMemberValue(member); } } // create DbParameter command.SetParameterValue(result, parameterBinding, translator); } // Add rows affected parameter command.RegisterRowsAffectedParameter(functionMapping.RowsAffectedParameter); }
/// <summary> /// Specialization of <see cref="CreatePlaceholder" /> for an entity set extent. /// </summary> /// <param name="entitySet"> </param> /// <returns> </returns> private PropagatorResult CreateEntitySetPlaceholder(EntitySet entitySet) { DebugCheck.NotNull(entitySet); var members = entitySet.ElementType.Properties; var memberValues = new PropagatorResult[members.Count]; for (var ordinal = 0; ordinal < members.Count; ordinal++) { var memberValue = CreateMemberPlaceholder(members[ordinal]); memberValues[ordinal] = memberValue; } var result = PropagatorResult.CreateStructuralValue(memberValues, entitySet.ElementType, false); return result; }
// 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(DbExpressionBinding target, EdmProperty property, PropagatorResult value) { DebugCheck.NotNull(target); DebugCheck.NotNull(property); DebugCheck.NotNull(value); var propertyExpression = GeneratePropertyExpression(target, property); var valueExpression = GenerateValueExpression(property, value); if (valueExpression.ExpressionKind == DbExpressionKind.Null) { return(propertyExpression.IsNull()); } return(propertyExpression.Equal(valueExpression)); }
/// <summary> /// Converts a record to a propagator result /// </summary> /// <param name="stateEntry">state manager entry containing the record</param> /// <param name="isModified">Indicates whether the root element is modified (i.e., whether the type has changed)</param> /// <param name="record">Record to convert</param> /// <param name="useCurrentValues">Indicates whether we are retrieving current or original values.</param> /// <param name="translator">Translator for session context; registers new metadata for the record type if none /// exists</param> /// <param name="modifiedPropertiesBehavior">Indicates how to determine whether a property is modified.</param> /// <returns>Result corresponding to the given record</returns> internal static PropagatorResult ExtractResultFromRecord( IEntityStateEntry stateEntry, bool isModified, IExtendedDataRecord record, bool useCurrentValues, UpdateTranslator translator, ModifiedPropertiesBehavior modifiedPropertiesBehavior) { var structuralType = (StructuralType)record.DataRecordInfo.RecordType.EdmType; var metadata = translator.GetExtractorMetadata(stateEntry.EntitySet, structuralType); var key = stateEntry.EntityKey; var nestedValues = new PropagatorResult[record.FieldCount]; for (var ordinal = 0; ordinal < nestedValues.Length; ordinal++) { nestedValues[ordinal] = metadata.RetrieveMember( stateEntry, record, useCurrentValues, key, ordinal, modifiedPropertiesBehavior); } return PropagatorResult.CreateStructuralValue(nestedValues, structuralType, isModified); }