// <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( ModificationFunctionMapping functionMapping, UpdateTranslator translator, ReadOnlyCollection<IEntityStateEntry> stateEntries, ExtractedStateEntry stateEntry) : this(translator, stateEntries, stateEntry, translator.GenerateCommandDefinition(functionMapping).CreateCommand()) { DebugCheck.NotNull(functionMapping); DebugCheck.NotNull(translator); DebugCheck.NotNull(stateEntries); }
private DbModificationCommandTree ConvertInternal( DbDeleteCommandTree commandTree) { this._currentFunctionMapping = this._entityTypeModificationFunctionMapping != null ? this._entityTypeModificationFunctionMapping.DeleteFunctionMapping : this._associationSetModificationFunctionMapping.DeleteFunctionMapping; return((DbModificationCommandTree) new DbDeleteCommandTree(commandTree.MetadataWorkspace, commandTree.DataSpace, commandTree.Target, commandTree.Predicate.Accept <DbExpression>((DbExpressionVisitor <DbExpression>) this))); }
private void ConfigureParameters(ModificationFunctionMapping modificationStoredProcedureMapping) { foreach (var keyValue in _parameterNames) { var propertyPath = keyValue.Key.PropertyPath; var parameterName = keyValue.Value.Item1; var originalValueParameterName = keyValue.Value.Item2; var parameterBindings = modificationStoredProcedureMapping .ParameterBindings .Where( pb => // First, try and match scalar/complex/many-to-many binding (((pb.MemberPath.AssociationSetEnd == null) || pb.MemberPath.AssociationSetEnd.ParentAssociationSet.ElementType.IsManyToMany()) && propertyPath.Equals( new PropertyPath( pb.MemberPath.Members.OfType <EdmProperty>().Select(m => m.GetClrPropertyInfo())))) || // Otherwise, try and match IA FK bindings ((propertyPath.Count == 2) && (pb.MemberPath.AssociationSetEnd != null) && pb.MemberPath.Members.First().GetClrPropertyInfo().IsSameAs(propertyPath.Last()) && pb.MemberPath.AssociationSetEnd.ParentAssociationSet.AssociationSetEnds .Select(ae => ae.CorrespondingAssociationEndMember.GetClrPropertyInfo()) .Where(pi => pi != null) .Any(pi => pi.IsSameAs(propertyPath.First())))) .ToList(); if (parameterBindings.Count == 1) { var parameterBinding = parameterBindings.Single(); if (!string.IsNullOrWhiteSpace(originalValueParameterName)) { if (parameterBinding.IsCurrent) { throw Error.ModificationFunctionParameterNotFoundOriginal( propertyPath, modificationStoredProcedureMapping.Function.FunctionName); } } parameterBinding.Parameter.Name = parameterName; _configuredParameters.Add(parameterBinding.Parameter); } else if (parameterBindings.Count == 2) { var parameterBinding = ((parameterBindings .Select(pb => pb.IsCurrent) .Distinct() .Count() == 1) && // same value for both parameterBindings .All(pb => pb.MemberPath.AssociationSetEnd != null)) ? !keyValue.Key.IsRightKey ? parameterBindings.First() : parameterBindings.Last() : parameterBindings.Single(pb => pb.IsCurrent); parameterBinding.Parameter.Name = parameterName; _configuredParameters.Add(parameterBinding.Parameter); if (!string.IsNullOrWhiteSpace(originalValueParameterName)) { parameterBinding = parameterBindings.Single(pb => !pb.IsCurrent); parameterBinding.Parameter.Name = originalValueParameterName; _configuredParameters.Add(parameterBinding.Parameter); } } else { throw Error.ModificationFunctionParameterNotFound( propertyPath, modificationStoredProcedureMapping.Function.FunctionName); } } var unconfiguredParameters = modificationStoredProcedureMapping .Function .Parameters .Except(_configuredParameters); foreach (var parameter in unconfiguredParameters) { parameter.Name = modificationStoredProcedureMapping .Function .Parameters .Except(new[] { parameter }) .UniquifyName(parameter.Name); } }
// 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); }
private ModificationFunctionMapping GenerateFunctionMapping( ModificationOperator modificationOperator, EntitySetBase entitySetBase, EntityTypeBase entityTypeBase, DbDatabaseMapping databaseMapping, IEnumerable <EdmProperty> parameterProperties, IEnumerable <Tuple <ModificationFunctionMemberPath, EdmProperty> > iaFkProperties, IList <ColumnMappingBuilder> columnMappings, IEnumerable <EdmProperty> resultProperties = null, string functionNamePrefix = null) { DebugCheck.NotNull(entitySetBase); DebugCheck.NotNull(entityTypeBase); DebugCheck.NotNull(databaseMapping); DebugCheck.NotNull(parameterProperties); DebugCheck.NotNull(iaFkProperties); DebugCheck.NotNull(columnMappings); var useOriginalValues = modificationOperator == ModificationOperator.Delete; var parameterMappingGenerator = new FunctionParameterMappingGenerator(_providerManifest); var parameterBindings = parameterMappingGenerator .Generate( modificationOperator == ModificationOperator.Insert && IsTableSplitDependent(entityTypeBase, databaseMapping) ? ModificationOperator.Update : modificationOperator, parameterProperties, columnMappings, new List <EdmProperty>(), useOriginalValues) .Concat( parameterMappingGenerator .Generate(iaFkProperties, useOriginalValues)) .ToList(); var parameters = parameterBindings.Select(b => b.Parameter).ToList(); UniquifyParameterNames(parameters); var functionPayload = new EdmFunctionPayload { ReturnParameters = new FunctionParameter[0], Parameters = parameters.ToArray(), IsComposable = false }; var function = databaseMapping.Database .AddFunction( (functionNamePrefix ?? entityTypeBase.Name) + "_" + modificationOperator.ToString(), functionPayload); var functionMapping = new ModificationFunctionMapping( entitySetBase, entityTypeBase, function, parameterBindings, null, resultProperties != null ? resultProperties.Select( p => new ModificationFunctionResultBinding( columnMappings.First(cm => cm.PropertyPath.SequenceEqual(new[] { p })).ColumnProperty.Name, p)) : null); return(functionMapping); }
public void WriteAssociationSetMapping_should_write_modification_function_mapping() { var fixture = new Fixture(); var entityType = new EntityType("E", "N", DataSpace.CSpace); var entitySet = new EntitySet("ES", "S", null, null, entityType); new EntityContainer("EC", DataSpace.SSpace).AddEntitySetBase(entitySet); var associationSet = new AssociationSet("AS", new AssociationType("A", XmlConstants.ModelNamespace_3, false, DataSpace.CSpace)); var associationEndMember1 = new AssociationEndMember("Source", new EntityType("E", "N", DataSpace.CSpace)); associationSet.AddAssociationSetEnd(new AssociationSetEnd(entitySet, associationSet, associationEndMember1)); var associationEndMember2 = new AssociationEndMember("Target", new EntityType("E", "N", DataSpace.CSpace)); associationSet.AddAssociationSetEnd(new AssociationSetEnd(entitySet, associationSet, associationEndMember2)); var storageModificationFunctionMapping = new ModificationFunctionMapping( associationSet, associationSet.ElementType, new EdmFunction("F", "N", DataSpace.SSpace, new EdmFunctionPayload()), new[] { new ModificationFunctionParameterBinding( new FunctionParameter( "P", TypeUsage.Create(PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32)), ParameterMode.In), new ModificationFunctionMemberPath( new EdmMember[] { new EdmProperty("K"), associationEndMember1 }, associationSet), true), new ModificationFunctionParameterBinding( new FunctionParameter( "P", TypeUsage.Create(PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32)), ParameterMode.In), new ModificationFunctionMemberPath( new EdmMember[] { new EdmProperty("K"), associationEndMember2 }, associationSet), false) }, null, null); var associationSetMapping = new AssociationSetMapping( associationSet, entitySet) { SourceEndMapping = new EndPropertyMapping { AssociationEnd = associationEndMember1 }, TargetEndMapping = new EndPropertyMapping { AssociationEnd = associationEndMember2 }, ModificationFunctionMapping = new AssociationSetModificationFunctionMapping( associationSet, storageModificationFunctionMapping, storageModificationFunctionMapping) }; fixture.Writer.WriteAssociationSetMappingElement(associationSetMapping); Assert.Equal( @"<AssociationSetMapping Name=""AS"" TypeName="".A"" StoreEntitySet=""E""> <EndProperty Name=""Source"" /> <EndProperty Name=""Target"" /> <ModificationFunctionMapping> <InsertFunction FunctionName=""N.F""> <EndProperty Name=""Source""> <ScalarProperty Name=""K"" ParameterName=""P"" Version=""Current"" /> </EndProperty> <EndProperty Name=""Target""> <ScalarProperty Name=""K"" ParameterName=""P"" Version=""Original"" /> </EndProperty> </InsertFunction> <DeleteFunction FunctionName=""N.F""> <EndProperty Name=""Source""> <ScalarProperty Name=""K"" ParameterName=""P"" Version=""Current"" /> </EndProperty> <EndProperty Name=""Target""> <ScalarProperty Name=""K"" ParameterName=""P"" Version=""Original"" /> </EndProperty> </DeleteFunction> </ModificationFunctionMapping> </AssociationSetMapping>", fixture.ToString()); }
// 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); }