Example #1
0
            public void Returns_rows_affected_when_there_are_no_result_columns()
            {
                var stateEntries = new ReadOnlyCollection <IEntityStateEntry>(new List <IEntityStateEntry>());
                var stateEntry   = new ExtractedStateEntry(
                    EntityState.Unchanged, PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value: 0),
                    PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value: 0),
                    new Mock <IEntityStateEntry>().Object);

                var timeout = 43;
                var updateTranslatorMock = new Mock <UpdateTranslator>();

                updateTranslatorMock.Setup(m => m.CommandTimeout).Returns(timeout);
                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 mockFunctionUpdateCommand = new Mock <FunctionUpdateCommand>(
                    updateTranslatorMock.Object, stateEntries, stateEntry, dbCommandMock.Object)
                {
                    CallBase = true
                };

                var timesCommandTimeoutCalled = 0;

                dbCommandMock.SetupSet(m => m.CommandTimeout = It.IsAny <int>()).Callback(
                    (int value) =>
                {
                    timesCommandTimeoutCalled++;
                    Assert.Equal(timeout, value);
                });

                var rowsAffected = 36;

                dbCommandMock.Setup(m => m.ExecuteNonQuery()).Returns(rowsAffected);

                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 rowsAffectedResult = mockFunctionUpdateCommand.Object.Execute(identifierValues, generatedValues);

                Assert.Equal(rowsAffected, rowsAffectedResult);
                Assert.Equal(1, timesCommandTimeoutCalled);
                Assert.Equal(1, timesSetInputIdentifiers);
                Assert.Equal(0, generatedValues.Count);
            }
        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())
 {
     DebugCheck.NotNull(functionMapping);
     DebugCheck.NotNull(translator);
     DebugCheck.NotNull(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,
     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);
 }
Example #5
0
 protected FunctionUpdateCommand(
     UpdateTranslator translator,
     ReadOnlyCollection <System.Data.Entity.Core.IEntityStateEntry> stateEntries,
     ExtractedStateEntry stateEntry,
     DbCommand dbCommand)
     : base(translator, stateEntry.Original, stateEntry.Current)
 {
     this._stateEntries = stateEntries;
     this._dbCommand    = (DbCommand) new InterceptableDbCommand(dbCommand, translator.InterceptionContext, (DbDispatchers)null);
 }
            internal override FunctionUpdateCommand Translate(
                UpdateTranslator translator,
                ExtractedStateEntry stateEntry)
            {
                if (null == m_mapping)
                {
                    return(null);
                }

                var 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
                var functionMapping = isInsert ? m_mapping.InsertFunctionMapping : m_mapping.DeleteFunctionMapping;
                var command         = new FunctionUpdateCommand(
                    functionMapping, translator, new ReadOnlyCollection <IEntityStateEntry>(new[] { stateEntry.Source }.ToList()), stateEntry);

                // extract the relationship values from the state entry
                PropagatorResult recordResult;

                if (isInsert)
                {
                    recordResult = stateEntry.Current;
                }
                else
                {
                    recordResult = stateEntry.Original;
                }

                // bind parameters
                foreach (var 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");

                    var keyProperty = (EdmProperty)parameterBinding.MemberPath.Members[0];
                    var endMember   = (AssociationEndMember)parameterBinding.MemberPath.Members[1];

                    // get the end member
                    var endResult = recordResult.GetMemberValue(endMember);
                    var 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,
     ReadOnlyCollection<IEntityStateEntry> stateEntries,
     ExtractedStateEntry stateEntry)
     : this(translator, stateEntries, stateEntry,
         translator.GenerateCommandDefinition(functionMapping).CreateCommand())
 {
     DebugCheck.NotNull(functionMapping);
     DebugCheck.NotNull(translator);
     DebugCheck.NotNull(stateEntries);
 }
Example #8
0
        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 = new InterceptableDbCommand(dbCommand, translator.InterceptionContext);
        }
            public void Returns_rows_affected_when_there_are_no_result_columns()
            {
                var stateEntries = new ReadOnlyCollection<IEntityStateEntry>(new List<IEntityStateEntry>());
                var stateEntry = new ExtractedStateEntry(
                    EntityState.Unchanged, PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value: 0),
                    PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, value: 0),
                    new Mock<IEntityStateEntry>().Object);

                var timeout = 43;
                var updateTranslatorMock = new Mock<UpdateTranslator>();
                updateTranslatorMock.Setup(m => m.CommandTimeout).Returns(timeout);
                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 mockFunctionUpdateCommand = new Mock<FunctionUpdateCommand>(
                    updateTranslatorMock.Object, stateEntries, stateEntry, dbCommandMock.Object)
                                                    {
                                                        CallBase = true
                                                    };

                var timesCommandTimeoutCalled = 0;
                dbCommandMock.SetupSet(m => m.CommandTimeout = It.IsAny<int>()).Callback(
                    (int value) =>
                        {
                            timesCommandTimeoutCalled++;
                            Assert.Equal(timeout, value);
                        });

                var rowsAffected = 36;
                dbCommandMock.Setup(m => m.ExecuteNonQuery()).Returns(rowsAffected);

                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 rowsAffectedResult = mockFunctionUpdateCommand.Object.Execute(identifierValues, generatedValues);

                Assert.Equal(rowsAffected, rowsAffectedResult);
                Assert.Equal(1, timesCommandTimeoutCalled);
                Assert.Equal(1, timesSetInputIdentifiers);
                Assert.Equal(0, generatedValues.Count);
            }
            private Tuple <EntityTypeModificationFunctionMapping, ModificationFunctionMapping> GetFunctionMapping(
                ExtractedStateEntry stateEntry)
            {
                // choose mapping based on type and operation
                ModificationFunctionMapping functionMapping;
                EntityType entityType;

                if (null != stateEntry.Current)
                {
                    entityType = (EntityType)stateEntry.Current.StructuralType;
                }
                else
                {
                    entityType = (EntityType)stateEntry.Original.StructuralType;
                }
                var 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));
            }
Example #11
0
            public void OperationCanceledException_thrown_if_task_is_cancelled()
            {
                var mockTranslator = new Mock <UpdateTranslator>();

                mockTranslator.Setup(t => t.InterceptionContext).Returns(new DbInterceptionContext());

                var stateEntry = new ExtractedStateEntry((EntityState)(-1), null, null, null);

                var functionUpdateCommand =
                    new Mock <FunctionUpdateCommand>(mockTranslator.Object, new List <IEntityStateEntry>().AsReadOnly(),
                                                     stateEntry, new Mock <DbCommand>().Object)
                {
                    CallBase = true
                }.Object;

                Assert.Throws <OperationCanceledException>(
                    () => functionUpdateCommand.ExecuteAsync(null, null, new CancellationToken(canceled: true))
                    .GetAwaiter().GetResult());
            }
 private static void BindFunctionParameters(
     UpdateTranslator translator,
     ExtractedStateEntry stateEntry,
     ModificationFunctionMapping functionMapping,
     FunctionUpdateCommand command,
     Dictionary <AssociationEndMember, IEntityStateEntry> currentReferenceEnds,
     Dictionary <AssociationEndMember, IEntityStateEntry> originalReferenceEnds)
 {
     foreach (ModificationFunctionParameterBinding parameterBinding in functionMapping.ParameterBindings)
     {
         PropagatorResult result;
         if (parameterBinding.MemberPath.AssociationSetEnd != null)
         {
             AssociationEndMember associationEndMember = parameterBinding.MemberPath.AssociationSetEnd.CorrespondingAssociationEndMember;
             IEntityStateEntry    stateEntry1;
             if (!(parameterBinding.IsCurrent ? currentReferenceEnds.TryGetValue(associationEndMember, out stateEntry1) : originalReferenceEnds.TryGetValue(associationEndMember, out stateEntry1)))
             {
                 if (associationEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One)
                 {
                     throw new UpdateException(Strings.Update_MissingRequiredRelationshipValue((object)stateEntry.Source.EntitySet.Name, (object)parameterBinding.MemberPath.AssociationSetEnd.ParentAssociationSet.Name), (Exception)null, command.GetStateEntries(translator).Cast <ObjectStateEntry>().Distinct <ObjectStateEntry>());
                 }
                 result = PropagatorResult.CreateSimpleValue(PropagatorFlags.NoFlags, (object)null);
             }
             else
             {
                 result = (parameterBinding.IsCurrent ? translator.RecordConverter.ConvertCurrentValuesToPropagatorResult(stateEntry1, ModifiedPropertiesBehavior.AllModified) : translator.RecordConverter.ConvertOriginalValuesToPropagatorResult(stateEntry1, ModifiedPropertiesBehavior.AllModified)).GetMemberValue((EdmMember)associationEndMember).GetMemberValue(parameterBinding.MemberPath.Members[0]);
             }
         }
         else
         {
             result = parameterBinding.IsCurrent ? stateEntry.Current : stateEntry.Original;
             int count = parameterBinding.MemberPath.Members.Count;
             while (count > 0)
             {
                 --count;
                 EdmMember member = parameterBinding.MemberPath.Members[count];
                 result = result.GetMemberValue(member);
             }
         }
         command.SetParameterValue(result, parameterBinding, translator);
     }
     command.RegisterRowsAffectedParameter(functionMapping.RowsAffectedParameter);
 }
            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);
            }
Example #14
0
            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);
            }
            internal override FunctionUpdateCommand Translate(
                UpdateTranslator translator,
                ExtractedStateEntry stateEntry)
            {
                var mapping         = GetFunctionMapping(stateEntry);
                var functionMapping = mapping.Item2;
                var 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
                    command = null;
                }
                else
                {
                    command = new FunctionUpdateCommand(functionMapping, translator, new ReadOnlyCollection <IEntityStateEntry>(stateEntries.ToList()), stateEntry);

                    // bind all function parameters
                    BindFunctionParameters(translator, stateEntry, functionMapping, command, currentReferenceEnds, originalReferenceEnds);

                    // interpret all result bindings
                    if (null != functionMapping.ResultBindings)
                    {
                        foreach (var resultBinding in functionMapping.ResultBindings)
                        {
                            var result = stateEntry.Current.GetMemberValue(resultBinding.Property);
                            command.AddResultColumn(translator, resultBinding.ColumnName, result);
                        }
                    }
                }

                return(command);
            }
 private Tuple<EntityTypeModificationFunctionMapping, ModificationFunctionMapping> GetFunctionMapping(
     ExtractedStateEntry stateEntry)
 {
     // choose mapping based on type and operation
     ModificationFunctionMapping functionMapping;
     EntityType entityType;
     if (null != stateEntry.Current)
     {
         entityType = (EntityType)stateEntry.Current.StructuralType;
     }
     else
     {
         entityType = (EntityType)stateEntry.Original.StructuralType;
     }
     var 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);
 }
 // <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);
            // 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);
            }
            // 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);
            }
            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);
            }
            public void OperationCanceledException_thrown_if_task_is_cancelled()
            {
                var mockTranslator = new Mock<UpdateTranslator>();
                mockTranslator.Setup(t => t.InterceptionContext).Returns(new DbInterceptionContext());

                var stateEntry = new ExtractedStateEntry((EntityState)(-1), null, null, null);

                var functionUpdateCommand = 
                    new Mock<FunctionUpdateCommand>(mockTranslator.Object, new List<IEntityStateEntry>().AsReadOnly(), 
                        stateEntry, new Mock<DbCommand>().Object)
                {
                    CallBase = true
                }.Object;

                Assert.Throws<OperationCanceledException>(
                    () => functionUpdateCommand.ExecuteAsync(null, null, new CancellationToken(canceled: true))
                        .GetAwaiter().GetResult());
            }
 // <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;
                }

                var 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
                var functionMapping = isInsert ? m_mapping.InsertFunctionMapping : m_mapping.DeleteFunctionMapping;
                var command = new FunctionUpdateCommand(
                    functionMapping, translator, new ReadOnlyCollection<IEntityStateEntry>(new[] { stateEntry.Source }.ToList()), stateEntry);

                // extract the relationship values from the state entry
                PropagatorResult recordResult;
                if (isInsert)
                {
                    recordResult = stateEntry.Current;
                }
                else
                {
                    recordResult = stateEntry.Original;
                }

                // bind parameters
                foreach (var 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");

                    var keyProperty = (EdmProperty)parameterBinding.MemberPath.Members[0];
                    var endMember = (AssociationEndMember)parameterBinding.MemberPath.Members[1];

                    // get the end member
                    var endResult = recordResult.GetMemberValue(endMember);
                    var keyResult = endResult.GetMemberValue(keyProperty);

                    command.SetParameterValue(keyResult, parameterBinding, translator);
                }
                // add rows affected output parameter
                command.RegisterRowsAffectedParameter(functionMapping.RowsAffectedParameter);

                return command;
            }
            internal override FunctionUpdateCommand Translate(
                UpdateTranslator translator,
                ExtractedStateEntry stateEntry)
            {
                var mapping = GetFunctionMapping(stateEntry);
                var functionMapping = mapping.Item2;
                var 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
                    command = null;
                }
                else
                {
                    command = new FunctionUpdateCommand(functionMapping, translator, new ReadOnlyCollection<IEntityStateEntry>(stateEntries.ToList()), stateEntry);

                    // bind all function parameters
                    BindFunctionParameters(translator, stateEntry, functionMapping, command, currentReferenceEnds, originalReferenceEnds);

                    // interpret all result bindings
                    if (null != functionMapping.ResultBindings)
                    {
                        foreach (var resultBinding in functionMapping.ResultBindings)
                        {
                            var result = stateEntry.Current.GetMemberValue(resultBinding.Property);
                            command.AddResultColumn(translator, resultBinding.ColumnName, result);
                        }
                    }
                }

                return command;
            }