internal override FunctionUpdateCommand Translate( UpdateTranslator translator, ExtractedStateEntry stateEntry) { var mapping = GetFunctionMapping(stateEntry); StorageEntityTypeModificationFunctionMapping typeMapping = mapping.Item1; StorageModificationFunctionMapping functionMapping = mapping.Item2; EntityKey entityKey = stateEntry.Source.EntityKey; var stateEntries = new HashSet<IEntityStateEntry> { stateEntry.Source }; // gather all referenced association ends var collocatedEntries = // find all related entries corresponding to collocated association types from end in functionMapping.CollocatedAssociationSetEnds join candidateEntry in translator.GetRelationships(entityKey) on end.CorrespondingAssociationEndMember.DeclaringType equals candidateEntry.EntitySet.ElementType select Tuple.Create(end.CorrespondingAssociationEndMember, candidateEntry); var currentReferenceEnds = new Dictionary<AssociationEndMember, IEntityStateEntry>(); var originalReferenceEnds = new Dictionary<AssociationEndMember, IEntityStateEntry>(); foreach (var candidate in collocatedEntries) { ProcessReferenceCandidate(entityKey, stateEntries, currentReferenceEnds, originalReferenceEnds, candidate.Item1, candidate.Item2); } // create function object FunctionUpdateCommand command; // consider the following scenario, we need to loop through all the state entries that is correlated with entity2 and make sure it is not changed. // entity1 <-- Independent Association <-- entity2 <-- Fk association <-- entity 3 // | // entity4 <-- Fk association <-- if (stateEntries.All(e => e.State == EntityState.Unchanged)) { // we shouldn't update the entity if it is unchanged, only update when referenced association is changed. // if not, then this will trigger a fake update for principal end as describe in bug 894569. command = null; } else { command = new FunctionUpdateCommand(functionMapping, translator, stateEntries.ToList().AsReadOnly(), stateEntry); // bind all function parameters BindFunctionParameters(translator, stateEntry, functionMapping, command, currentReferenceEnds, originalReferenceEnds); // interpret all result bindings if (null != functionMapping.ResultBindings) { foreach (StorageModificationFunctionResultBinding resultBinding in functionMapping.ResultBindings) { PropagatorResult result = stateEntry.Current.GetMemberValue(resultBinding.Property); command.AddResultColumn(translator, resultBinding.ColumnName, result); } } } return command; }
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)); } } } } }
protected FunctionUpdateCommand( UpdateTranslator translator, ReadOnlyCollection<IEntityStateEntry> stateEntries, ExtractedStateEntry stateEntry, DbCommand dbCommand) : base(translator, stateEntry.Original, stateEntry.Current) { // populate the main state entry for error reporting _stateEntries = stateEntries; _dbCommand = dbCommand; }
/// <summary> /// Initialize a new function command. Initializes the command object. /// </summary> /// <param name="functionMapping">Function mapping metadata</param> /// <param name="translator">Translator</param> /// <param name="stateEntries">State entries handled by this operation.</param> /// <param name="stateEntry">'Root' state entry being handled by this function.</param> internal FunctionUpdateCommand( StorageModificationFunctionMapping functionMapping, UpdateTranslator translator, ReadOnlyCollection<IEntityStateEntry> stateEntries, ExtractedStateEntry stateEntry) : this(translator, stateEntries, stateEntry, translator.GenerateCommandDefinition(functionMapping).CreateCommand()) { //Contract.Requires(functionMapping != null); //Contract.Requires(translator != null); //Contract.Requires(stateEntries != null); }
/// <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) { Debug.Assert(null != source); Debug.Assert(null != translator); Debug.Assert(null != sourceTable); SourceInterpreter interpreter = new SourceInterpreter(translator, sourceTable); interpreter.RetrieveResultMarkup(source); return new ReadOnlyCollection<IEntityStateEntry>(interpreter.m_stateEntries); }
/// <summary> /// Initialize a new function command. Initializes the command object. /// </summary> /// <param name="functionMapping">Function mapping metadata</param> /// <param name="translator">Translator</param> /// <param name="stateEntries">State entries handled by this operation.</param> /// <param name="stateEntry">'Root' state entry being handled by this function.</param> internal FunctionUpdateCommand(StorageModificationFunctionMapping functionMapping, UpdateTranslator translator, System.Collections.ObjectModel.ReadOnlyCollection<IEntityStateEntry> stateEntries, ExtractedStateEntry stateEntry) : base(stateEntry.Original, stateEntry.Current) { EntityUtil.CheckArgumentNull(functionMapping, "functionMapping"); EntityUtil.CheckArgumentNull(translator, "translator"); EntityUtil.CheckArgumentNull(stateEntries, "stateEntries"); // populate the main state entry for error reporting m_stateEntries = stateEntries; // create a command DbCommandDefinition commandDefinition = translator.GenerateCommandDefinition(functionMapping); m_dbCommand = commandDefinition.CreateCommand(); }
/// <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> /// <param name="translator">Translator context.</param> /// <returns>Cloned placeholder with key values</returns> internal static PropagatorResult Populate(PropagatorResult placeholder, CompositeKey key, CompositeKey placeholderKey, PopulateMode mode, UpdateTranslator translator) { EntityUtil.CheckArgumentNull(placeholder, "placeholder"); EntityUtil.CheckArgumentNull(key, "key"); EntityUtil.CheckArgumentNull(placeholderKey, "placeholderKey"); EntityUtil.CheckArgumentNull(translator, "translator"); // Figure out which flags to apply to generated elements. bool isNull = mode == PopulateMode.NullModified || mode == PopulateMode.NullPreserve; bool preserve = mode == PopulateMode.NullPreserve || mode == PopulateMode.Unknown; PropagatorFlags flags = PropagatorFlags.NoFlags; if (!isNull) { flags |= PropagatorFlags.Unknown; } // only null values are known if (preserve) { flags |= PropagatorFlags.Preserve; } PropagatorResult result = placeholder.Replace(node => { // See if this is a key element int keyIndex = -1; for (int 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 object value = isNull ? null : node.GetSimpleValue(); return PropagatorResult.CreateSimpleValue(flags, value); } }); return result; }
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 ExtractedStateEntry(UpdateTranslator translator, IEntityStateEntry stateEntry) { //Contract.Requires(translator != null); //Contract.Requires(stateEntry != null); 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: Contract.Assert(false, "Unexpected IEntityStateEntry.State for entity " + stateEntry.State); Original = null; Current = null; break; } }
internal ExtractedStateEntry(UpdateTranslator translator, IEntityStateEntry stateEntry) { Debug.Assert(null != stateEntry, "stateEntry must not be null"); this.State = stateEntry.State; this.Source = stateEntry; switch (stateEntry.State) { case EntityState.Deleted: this.Original = translator.RecordConverter.ConvertOriginalValuesToPropagatorResult( stateEntry, ModifiedPropertiesBehavior.AllModified); this.Current = null; break; case EntityState.Unchanged: this.Original = translator.RecordConverter.ConvertOriginalValuesToPropagatorResult( stateEntry, ModifiedPropertiesBehavior.NoneModified); this.Current = translator.RecordConverter.ConvertCurrentValuesToPropagatorResult( stateEntry, ModifiedPropertiesBehavior.NoneModified); break; case EntityState.Modified: this.Original = translator.RecordConverter.ConvertOriginalValuesToPropagatorResult( stateEntry, ModifiedPropertiesBehavior.SomeModified); this.Current = translator.RecordConverter.ConvertCurrentValuesToPropagatorResult( stateEntry, ModifiedPropertiesBehavior.SomeModified); break; case EntityState.Added: this.Original = null; this.Current = translator.RecordConverter.ConvertCurrentValuesToPropagatorResult( stateEntry, ModifiedPropertiesBehavior.AllModified); break; default: Debug.Fail("unexpected IEntityStateEntry.State for entity " + stateEntry.State); this.Original = null; this.Current = null; break; } }
/// <summary> /// Initialize an update compiler. /// </summary> /// <param name="translator">Update context.</param> internal UpdateCompiler(UpdateTranslator translator) { m_translator = translator; }
/// <summary> /// Persists stateManager changes to the store. /// </summary> /// <param name="stateManager">StateManager containing changes to persist.</param> /// <param name="adapter">Map adapter requesting the changes.</param> /// <returns>Total number of state entries affected</returns> internal static Int32 Update(IEntityStateManager stateManager, IEntityAdapter adapter) { // provider/connection details EntityConnection connection = (EntityConnection)adapter.Connection; MetadataWorkspace metadataWorkspace = connection.GetMetadataWorkspace(); int? commandTimeout = adapter.CommandTimeout; UpdateTranslator translator = new UpdateTranslator(stateManager, metadataWorkspace, connection, commandTimeout); // tracks values for identifiers in this session Dictionary<int, object> identifierValues = new Dictionary<int, object>(); // tracks values for generated values in this session List<KeyValuePair<PropagatorResult, object>> generatedValues = new List<KeyValuePair<PropagatorResult, object>>(); IEnumerable<UpdateCommand> orderedCommands = translator.ProduceCommands(); // used to track the source of commands being processed in case an exception is thrown UpdateCommand source = null; try { foreach (UpdateCommand command in orderedCommands) { // Remember the data sources so that we can throw meaningful exception source = command; long rowsAffected = command.Execute(translator, connection, identifierValues, generatedValues); translator.ValidateRowsAffected(rowsAffected, source); } } catch (Exception e) { // we should not be wrapping all exceptions if (UpdateTranslator.RequiresContext(e)) { throw EntityUtil.Update(System.Data.Entity.Strings.Update_GeneralExecutionException, e, translator.DetermineStateEntriesFromSource(source)); } throw; } translator.BackPropagateServerGen(generatedValues); int totalStateEntries = translator.AcceptChanges(adapter); return totalStateEntries; }
private SourceInterpreter(UpdateTranslator translator, EntitySet sourceTable) { m_stateEntries = new List<IEntityStateEntry>(); m_translator = translator; m_sourceTable = sourceTable; }
internal override long Execute(UpdateTranslator translator, EntityConnection connection, Dictionary<int, object> identifierValues, List<KeyValuePair<PropagatorResult, object>> generatedValues) { // Compile command using (DbCommand command = this.CreateCommand(translator, identifierValues)) { // configure command to use the connection and transaction for this session command.Transaction = ((null != connection.CurrentTransaction) ? connection.CurrentTransaction.StoreTransaction : null); command.Connection = connection.StoreConnection; if (translator.CommandTimeout.HasValue) { command.CommandTimeout = translator.CommandTimeout.Value; } // Execute the query int rowsAffected; if (m_modificationCommandTree.HasReader) { // retrieve server gen results rowsAffected = 0; using (DbDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess)) { if (reader.Read()) { rowsAffected++; IBaseList<EdmMember> members = TypeHelpers.GetAllStructuralMembers(this.CurrentValues.StructuralType); for (int ordinal = 0; ordinal < reader.FieldCount; ordinal++) { // column name of result corresponds to column name of table string columnName = reader.GetName(ordinal); EdmMember member = members[columnName]; object value; if (Helper.IsSpatialType(member.TypeUsage) && !reader.IsDBNull(ordinal)) { value = SpatialHelpers.GetSpatialValue(translator.MetadataWorkspace, reader, member.TypeUsage, ordinal); } else { value = reader.GetValue(ordinal); } // retrieve result which includes the context for back-propagation int columnOrdinal = members.IndexOf(member); PropagatorResult result = this.CurrentValues.GetMemberValue(columnOrdinal); // register for back-propagation generatedValues.Add(new KeyValuePair<PropagatorResult, object>(result, value)); // register identifier if it exists int identifier = result.Identifier; if (PropagatorResult.NullIdentifier != identifier) { identifierValues.Add(identifier, value); } } } // Consume the current reader (and subsequent result sets) so that any errors // executing the command can be intercepted CommandHelper.ConsumeReader(reader); } } else { rowsAffected = command.ExecuteNonQuery(); } return rowsAffected; } }
internal override IList<IEntityStateEntry> GetStateEntries(UpdateTranslator translator) { List<IEntityStateEntry> stateEntries = new List<IEntityStateEntry>(2); if (null != this.OriginalValues) { foreach (IEntityStateEntry stateEntry in SourceInterpreter.GetAllStateEntries( this.OriginalValues, translator, this.Table)) { stateEntries.Add(stateEntry); } } if (null != this.CurrentValues) { foreach (IEntityStateEntry stateEntry in SourceInterpreter.GetAllStateEntries( this.CurrentValues, translator, this.Table)) { stateEntries.Add(stateEntry); } } return stateEntries; }
/// <summary> /// Creates a record for an extent containing default values. Assumes the extent is either /// a relationship set or an entity set. /// </summary> /// <remarks> /// Each scalar value appearing in the record is a <see cref="DbConstantExpression" />. A placeholder is created by recursively /// building a record, so an entity record type will return a new record (<see cref="DbNewInstanceExpression" />) /// consisting of some recursively built record for each column in the type. /// </remarks> /// <param name="extent">Extent</param> /// <param name="parent">Command tree used to generate portions of the record</param> /// <returns>A default record for the </returns> internal static PropagatorResult CreatePlaceholder(EntitySetBase extent, UpdateTranslator parent) { EntityUtil.CheckArgumentNull(extent, "extent"); ExtentPlaceholderCreator creator = new ExtentPlaceholderCreator(parent); AssociationSet associationSet = extent as AssociationSet; if (null != associationSet) { return creator.CreateAssociationSetPlaceholder(associationSet); } EntitySet entitySet = extent as EntitySet; if (null != entitySet) { return creator.CreateEntitySetPlaceholder(entitySet); } throw EntityUtil.NotSupported(System.Data.Entity.Strings.Update_UnsupportedExtentType( extent.Name, extent.GetType().Name)); }
private int GetColumnOrdinal(UpdateTranslator translator, DbDataReader reader, string columnName) { int columnOrdinal; try { columnOrdinal = reader.GetOrdinal(columnName); } catch (IndexOutOfRangeException) { throw EntityUtil.Update(System.Data.Entity.Strings.Update_MissingResultColumn(columnName), null, this.GetStateEntries(translator)); } return columnOrdinal; }
// efects: Executes the current function command in the given transaction and connection context. // All server-generated values are added to the generatedValues list. If those values are identifiers, they are // also added to the identifierValues dictionary, which associates proxy identifiers for keys in the session // with their actual values, permitting fix-up of identifiers across relationships. internal override long Execute(UpdateTranslator translator, EntityConnection connection, Dictionary<int, object> identifierValues, List<KeyValuePair<PropagatorResult, object>> generatedValues) { // configure command to use the connection and transaction for this session m_dbCommand.Transaction = ((null != connection.CurrentTransaction) ? connection.CurrentTransaction.StoreTransaction : null); m_dbCommand.Connection = connection.StoreConnection; if (translator.CommandTimeout.HasValue) { m_dbCommand.CommandTimeout = translator.CommandTimeout.Value; } // set all identifier inputs (to support propagation of identifier values across relationship // boundaries) if (null != m_inputIdentifiers) { foreach (KeyValuePair<int, DbParameter> inputIdentifier in m_inputIdentifiers) { object value; if (identifierValues.TryGetValue(inputIdentifier.Key, out value)) { // set the actual value for the identifier if it has been produced by some // other command inputIdentifier.Value.Value = value; } } } // Execute the query long rowsAffected; if (null != m_resultColumns) { // If there are result columns, read the server gen results rowsAffected = 0; IBaseList<EdmMember> members = TypeHelpers.GetAllStructuralMembers(this.CurrentValues.StructuralType); using (DbDataReader reader = m_dbCommand.ExecuteReader(CommandBehavior.SequentialAccess)) { // Retrieve only the first row from the first result set if (reader.Read()) { rowsAffected++; foreach (var resultColumn in m_resultColumns .Select(r => new KeyValuePair<int, PropagatorResult>(GetColumnOrdinal(translator, reader, r.Key), r.Value)) .OrderBy(r => r.Key)) // order by column ordinal to avoid breaking SequentialAccess readers { int columnOrdinal = resultColumn.Key; TypeUsage columnType = members[resultColumn.Value.RecordOrdinal].TypeUsage; object value; if (Helper.IsSpatialType(columnType) && !reader.IsDBNull(columnOrdinal)) { value = SpatialHelpers.GetSpatialValue(translator.MetadataWorkspace, reader, columnType, columnOrdinal); } else { value = reader.GetValue(columnOrdinal); } // register for back-propagation PropagatorResult result = resultColumn.Value; generatedValues.Add(new KeyValuePair<PropagatorResult, object>(result, value)); // register identifier if it exists int identifier = result.Identifier; if (PropagatorResult.NullIdentifier != identifier) { identifierValues.Add(identifier, value); } } } // Consume the current reader (and subsequent result sets) so that any errors // executing the function can be intercepted CommandHelper.ConsumeReader(reader); } } else { rowsAffected = m_dbCommand.ExecuteNonQuery(); } // if an explicit rows affected parameter exists, use this value instead if (null != m_rowsAffectedParameter) { // by design, negative row counts indicate failure iff. an explicit rows // affected parameter is used if (DBNull.Value.Equals(m_rowsAffectedParameter.Value)) { rowsAffected = 0; } else { try { rowsAffected = Convert.ToInt64(m_rowsAffectedParameter.Value, CultureInfo.InvariantCulture); } catch (Exception e) { if (UpdateTranslator.RequiresContext(e)) { // wrap the exception throw EntityUtil.Update(System.Data.Entity.Strings.Update_UnableToConvertRowsAffectedParameterToInt32( m_rowsAffectedParameter.ParameterName, typeof(int).FullName), e, this.GetStateEntries(translator)); } throw; } } } return rowsAffected; }
// 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 == m_resultColumns) { m_resultColumns = new List<KeyValuePair<string, PropagatorResult>>(initializeSize); } m_resultColumns.Add(new KeyValuePair<string, PropagatorResult>(columnName, result)); int identifier = result.Identifier; if (PropagatorResult.NullIdentifier != identifier) { if (translator.KeyManager.HasPrincipals(identifier)) { throw EntityUtil.InvalidOperation(System.Data.Entity.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 DbParameter parameter = this.m_dbCommand.Parameters[parameterBinding.Parameter.Name]; TypeUsage parameterType = parameterBinding.Parameter.TypeUsage; object 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) int identifier = result.Identifier; if (PropagatorResult.NullIdentifier != identifier) { const int initialSize = 2; // expect on average less than two input identifiers per command if (null == m_inputIdentifiers) { m_inputIdentifiers = new List<KeyValuePair<int, DbParameter>>(initialSize); } foreach (int principal in translator.KeyManager.GetPrincipals(identifier)) { m_inputIdentifiers.Add(new KeyValuePair<int, DbParameter>(principal, parameter)); } } }
/// <summary> /// Gets state entries contributing to this function. Supports error reporting. /// </summary> internal override IList<IEntityStateEntry> GetStateEntries(UpdateTranslator translator) { return m_stateEntries; }
/// <summary> /// Initializes a new converter given a command tree context. Initializes a new record layout cache. /// </summary> /// <param name="updateTranslator">Sets <see cref="m_updateTranslator" /></param> internal RecordConverter(UpdateTranslator updateTranslator) { m_updateTranslator = updateTranslator; }
/// <summary> /// Gets DB command definition encapsulating store logic for this command. /// </summary> private DbCommand CreateCommand(UpdateTranslator translator, Dictionary<int, object> identifierValues) { DbModificationCommandTree commandTree = m_modificationCommandTree; // check if any server gen identifiers need to be set if (null != m_inputIdentifiers) { Dictionary<DbSetClause, DbSetClause> modifiedClauses = new Dictionary<DbSetClause, DbSetClause>(); for (int idx = 0; idx < m_inputIdentifiers.Count; idx++) { KeyValuePair<int, DbSetClause> inputIdentifier = m_inputIdentifiers[idx]; object value; if (identifierValues.TryGetValue(inputIdentifier.Key, out value)) { // reset the value of the identifier DbSetClause newClause = new DbSetClause(inputIdentifier.Value.Property, DbExpressionBuilder.Constant(value)); modifiedClauses[inputIdentifier.Value] = newClause; m_inputIdentifiers[idx] = new KeyValuePair<int, DbSetClause>(inputIdentifier.Key, newClause); } } commandTree = RebuildCommandTree(commandTree, modifiedClauses); } return translator.CreateCommand(commandTree); }
/// <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) { StructuralType structuralType = (StructuralType)record.DataRecordInfo.RecordType.EdmType; ExtractorMetadata metadata = translator.GetExtractorMetadata(stateEntry.EntitySet, structuralType); EntityKey key = stateEntry.EntityKey; PropagatorResult[] nestedValues = new PropagatorResult[record.FieldCount]; for (int ordinal = 0; ordinal < nestedValues.Length; ordinal++) { nestedValues[ordinal] = metadata.RetrieveMember(stateEntry, record, useCurrentValues, key, ordinal, modifiedPropertiesBehavior); } return PropagatorResult.CreateStructuralValue(nestedValues, structuralType, isModified); }
private int GetColumnOrdinal(UpdateTranslator translator, DbDataReader reader, string columnName) { int columnOrdinal; try { columnOrdinal = reader.GetOrdinal(columnName); } catch (IndexOutOfRangeException) { throw new UpdateException( Strings.Update_MissingResultColumn(columnName), null, GetStateEntries(translator).Cast<ObjectStateEntry>().Distinct()); } return columnOrdinal; }
internal override FunctionUpdateCommand Translate( UpdateTranslator translator, ExtractedStateEntry stateEntry) { if (null == m_mapping) { return null; } bool isInsert = EntityState.Added == stateEntry.State; EntityUtil.ValidateNecessaryModificationFunctionMapping( isInsert ? m_mapping.InsertFunctionMapping : m_mapping.DeleteFunctionMapping, isInsert ? "Insert" : "Delete", stateEntry.Source, "AssociationSet", m_mapping.AssociationSet.Name); // initialize a new command StorageModificationFunctionMapping functionMapping = isInsert ? m_mapping.InsertFunctionMapping : m_mapping.DeleteFunctionMapping; FunctionUpdateCommand command = new FunctionUpdateCommand(functionMapping, translator, new [] { stateEntry.Source }.ToList().AsReadOnly(), stateEntry); // extract the relationship values from the state entry PropagatorResult recordResult; if (isInsert) { recordResult = stateEntry.Current; } else { recordResult = stateEntry.Original; } // bind parameters foreach (StorageModificationFunctionParameterBinding parameterBinding in functionMapping.ParameterBindings) { // extract the relationship information Debug.Assert(2 == parameterBinding.MemberPath.Members.Count, "relationship parameter binding member " + "path should include the relationship end and key property only"); EdmProperty keyProperty = (EdmProperty)parameterBinding.MemberPath.Members[0]; AssociationEndMember endMember = (AssociationEndMember)parameterBinding.MemberPath.Members[1]; // get the end member PropagatorResult endResult = recordResult.GetMemberValue(endMember); PropagatorResult keyResult = endResult.GetMemberValue(keyProperty); command.SetParameterValue(keyResult, parameterBinding, translator); } // add rows affected output parameter command.RegisterRowsAffectedParameter(functionMapping.RowsAffectedParameter); return command; }
/// <summary> /// Requires: this translator must be registered to handle the entity set /// for the given state entry. /// /// Translates the given state entry to a command. /// </summary> /// <param name="translator">Parent update translator (global state for the workload)</param> /// <param name="stateEntry">State entry to translate. Must belong to the /// entity/association set handled by this translator</param> /// <returns>Command corresponding to the given state entry</returns> internal abstract FunctionUpdateCommand Translate( UpdateTranslator translator, ExtractedStateEntry stateEntry);
internal ExtractorMetadata(EntitySetBase entitySetBase, StructuralType type, UpdateTranslator translator) { EntityUtil.CheckArgumentNull(entitySetBase, "entitySetBase"); m_type = EntityUtil.CheckArgumentNull(type, "type"); m_translator = EntityUtil.CheckArgumentNull(translator, "translator"); EntityType entityType = null; Set<EdmMember> keyMembers; Set<EdmMember> foreignKeyMembers; switch (type.BuiltInTypeKind) { case BuiltInTypeKind.RowType: // for row types (which are actually association end key records in disguise), all members // are keys keyMembers = new Set<EdmMember>(((RowType)type).Properties).MakeReadOnly(); foreignKeyMembers = Set<EdmMember>.Empty; break; case BuiltInTypeKind.EntityType: entityType = (EntityType)type; keyMembers = new Set<EdmMember>(entityType.KeyMembers).MakeReadOnly(); foreignKeyMembers = new Set<EdmMember>(((EntitySet)entitySetBase).ForeignKeyDependents .SelectMany(fk => fk.Item2.ToProperties)).MakeReadOnly(); break; default: keyMembers = Set<EdmMember>.Empty; foreignKeyMembers = Set<EdmMember>.Empty; break; } IBaseList<EdmMember> members = TypeHelpers.GetAllStructuralMembers(type); m_memberMap = new MemberInformation[members.Count]; // for each member, cache expensive to compute metadata information for (int ordinal = 0; ordinal < members.Count; ordinal++) { EdmMember member = members[ordinal]; // figure out flags for this member PropagatorFlags flags = PropagatorFlags.NoFlags; int? entityKeyOrdinal = default(int?); if (keyMembers.Contains(member)) { flags |= PropagatorFlags.Key; if (null != entityType) { entityKeyOrdinal = entityType.KeyMembers.IndexOf(member); } } if (foreignKeyMembers.Contains(member)) { flags |= PropagatorFlags.ForeignKey; } if (MetadataHelper.GetConcurrencyMode(member) == ConcurrencyMode.Fixed) { flags |= PropagatorFlags.ConcurrencyValue; } // figure out whether this member is mapped to any server generated // columns in the store bool isServerGenerated = m_translator.ViewLoader.IsServerGen(entitySetBase, m_translator.MetadataWorkspace, member); // figure out whether member nullability is used as a condition in mapping bool isNullConditionMember = m_translator.ViewLoader.IsNullConditionMember(entitySetBase, m_translator.MetadataWorkspace, member); // add information about this member m_memberMap[ordinal] = new MemberInformation(ordinal, entityKeyOrdinal, flags, member, isServerGenerated, isNullConditionMember); } }
/// <summary> /// Constructs a new placeholder creator. /// </summary> /// <param name="parent">Context used to generate all elements of the placeholder.</param> private ExtentPlaceholderCreator(UpdateTranslator parent) { EntityUtil.CheckArgumentNull(parent, "parent"); m_parent = parent; }
// Walks through all parameter bindings in the function mapping and binds the parameters to the // requested properties of the given state entry. private void BindFunctionParameters(UpdateTranslator translator, ExtractedStateEntry stateEntry, StorageModificationFunctionMapping functionMapping, FunctionUpdateCommand command, Dictionary<AssociationEndMember, IEntityStateEntry> currentReferenceEnds, Dictionary<AssociationEndMember, IEntityStateEntry> originalReferenceEnds) { // bind all parameters foreach (StorageModificationFunctionParameterBinding parameterBinding in functionMapping.ParameterBindings) { PropagatorResult result; // extract value if (null != parameterBinding.MemberPath.AssociationSetEnd) { // find the relationship entry corresponding to the navigation AssociationEndMember endMember = parameterBinding.MemberPath.AssociationSetEnd.CorrespondingAssociationEndMember; IEntityStateEntry relationshipEntry; bool hasTarget = parameterBinding.IsCurrent ? currentReferenceEnds.TryGetValue(endMember, out relationshipEntry) : originalReferenceEnds.TryGetValue(endMember, out relationshipEntry); if (!hasTarget) { if (endMember.RelationshipMultiplicity == RelationshipMultiplicity.One) { string entitySetName = stateEntry.Source.EntitySet.Name; string associationSetName = parameterBinding.MemberPath.AssociationSetEnd.ParentAssociationSet.Name; throw EntityUtil.Update(Strings.Update_MissingRequiredRelationshipValue(entitySetName, associationSetName), null, command.GetStateEntries(translator)); } else { result = PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, null); } } else { // get the actual value PropagatorResult relationshipResult = parameterBinding.IsCurrent ? translator.RecordConverter.ConvertCurrentValuesToPropagatorResult(relationshipEntry, ModifiedPropertiesBehavior.AllModified) : translator.RecordConverter.ConvertOriginalValuesToPropagatorResult(relationshipEntry, ModifiedPropertiesBehavior.AllModified); PropagatorResult endResult = relationshipResult.GetMemberValue(endMember); EdmProperty 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 (int i = parameterBinding.MemberPath.Members.Count; i > 0;) { --i; EdmMember member = parameterBinding.MemberPath.Members[i]; result = result.GetMemberValue(member); } } // create DbParameter command.SetParameterValue(result, parameterBinding, translator); } // Add rows affected parameter command.RegisterRowsAffectedParameter(functionMapping.RowsAffectedParameter); }