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; }
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); }
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> /// 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> /// 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(); }
private void LoadStateEntry(IEntityStateEntry stateEntry) { Debug.Assert(null != stateEntry, "state entry must exist"); // make sure the state entry doesn't contain invalid data and register it with the // update pipeline ValidateAndRegisterStateEntry(stateEntry); // use data structure internal to the update pipeline instead of the raw state entry ExtractedStateEntry extractedStateEntry = new ExtractedStateEntry(this, stateEntry); // figure out if this state entry is being handled by a function (stored procedure) or // through dynamic SQL EntitySetBase extent = stateEntry.EntitySet; if (null == m_viewLoader.GetFunctionMappingTranslator(extent, m_metadataWorkspace)) { // if there is no function mapping, register a ChangeNode (used for update // propagation and dynamic SQL generation) ChangeNode changeNode = GetExtentModifications(extent); if (null != extractedStateEntry.Original) { changeNode.Deleted.Add(extractedStateEntry.Original); } if (null != extractedStateEntry.Current) { changeNode.Inserted.Add(extractedStateEntry.Current); } } else { // for function updates, store off the extracted state entry in its entirety // (used when producing FunctionUpdateCommands) List<ExtractedStateEntry> functionEntries = GetExtentFunctionModifications(extent); functionEntries.Add(extractedStateEntry); } }
/// <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 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; }
// 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); }
private Tuple<StorageEntityTypeModificationFunctionMapping, StorageModificationFunctionMapping> GetFunctionMapping(ExtractedStateEntry stateEntry) { // choose mapping based on type and operation StorageModificationFunctionMapping functionMapping; EntityType entityType; if (null != stateEntry.Current) { entityType = (EntityType)stateEntry.Current.StructuralType; } else { entityType = (EntityType)stateEntry.Original.StructuralType; } StorageEntityTypeModificationFunctionMapping typeMapping = m_typeMappings[entityType]; switch (stateEntry.State) { case EntityState.Added: functionMapping = typeMapping.InsertFunctionMapping; EntityUtil.ValidateNecessaryModificationFunctionMapping(functionMapping, "Insert", stateEntry.Source, "EntityType", entityType.Name); break; case EntityState.Deleted: functionMapping = typeMapping.DeleteFunctionMapping; EntityUtil.ValidateNecessaryModificationFunctionMapping(functionMapping, "Delete", stateEntry.Source, "EntityType", entityType.Name); break; case EntityState.Unchanged: case EntityState.Modified: functionMapping = typeMapping.UpdateFunctionMapping; EntityUtil.ValidateNecessaryModificationFunctionMapping(functionMapping, "Update", stateEntry.Source, "EntityType", entityType.Name); break; default: functionMapping = null; Debug.Fail("unexpected state"); break; } return Tuple.Create(typeMapping, functionMapping); }
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 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); }
private Tuple <StorageEntityTypeModificationFunctionMapping, StorageModificationFunctionMapping> GetFunctionMapping(ExtractedStateEntry stateEntry) { // choose mapping based on type and operation StorageModificationFunctionMapping functionMapping; EntityType entityType; if (null != stateEntry.Current) { entityType = (EntityType)stateEntry.Current.StructuralType; } else { entityType = (EntityType)stateEntry.Original.StructuralType; } StorageEntityTypeModificationFunctionMapping typeMapping = m_typeMappings[entityType]; switch (stateEntry.State) { case EntityState.Added: functionMapping = typeMapping.InsertFunctionMapping; EntityUtil.ValidateNecessaryModificationFunctionMapping(functionMapping, "Insert", stateEntry.Source, "EntityType", entityType.Name); break; case EntityState.Deleted: functionMapping = typeMapping.DeleteFunctionMapping; EntityUtil.ValidateNecessaryModificationFunctionMapping(functionMapping, "Delete", stateEntry.Source, "EntityType", entityType.Name); break; case EntityState.Unchanged: case EntityState.Modified: functionMapping = typeMapping.UpdateFunctionMapping; EntityUtil.ValidateNecessaryModificationFunctionMapping(functionMapping, "Update", stateEntry.Source, "EntityType", entityType.Name); break; default: functionMapping = null; Debug.Fail("unexpected state"); break; } return(Tuple.Create(typeMapping, functionMapping)); }