public async Task UpdateAsync_ShouldReturnCollectionWithOnlyIncludedPropertiesUpdated_WhenEntitiesMatchAndIncludedPropertyExpresionsArePassed(DbProvider provider, TestConfiguration testConfiguration = TestConfiguration.Default)
        {
            TestEntityCompositeKey[] existingEntities = new[]
            {
                new TestEntityCompositeKey {
                    IdPartA = "Should not be updated 1", IdPartB = "B", IntTestValue = 561645, BoolTestValue = false, DateTimeTestValue = DateTime.UtcNow, LongTestValue = 54123
                },
                new TestEntityCompositeKey {
                    IdPartA = "Should not be updated 2", IdPartB = "B", IntTestValue = 56123, BoolTestValue = true, DateTimeTestValue = DateTime.UtcNow, LongTestValue = 1231
                },
                new TestEntityCompositeKey {
                    IdPartA = "Should be updated 3", IdPartB = "B", IntTestValue = 111, BoolTestValue = true, DateTimeTestValue = DateTime.UtcNow, LongTestValue = 65465132165
                },
            };
            TestEntityCompositeKey[] expectedEntities = new[]
            {
                new TestEntityCompositeKey {
                    IdPartA = "Should not be updated 1", IdPartB = "B", IntTestValue = -1, BoolTestValue = true, DateTimeTestValue = DateTime.UtcNow.AddDays(1), LongTestValue = 781
                },
                new TestEntityCompositeKey {
                    IdPartA = "Should be updated 3", IdPartB = "B", IntTestValue = 561235164, BoolTestValue = false, DateTimeTestValue = DateTime.UtcNow.AddDays(1), LongTestValue = 165465132165
                },
            };
            using TestDbContext context = await ContextFactory.GetDbContextAsync(provider, seedData : existingEntities, testConfiguration : testConfiguration);

            // Include long and datetime values - they are the only items expected to be updated based on the mocked data.
            InclusionBuilder <TestEntityCompositeKey> inclusionBuilder = new InclusionBuilder <TestEntityCompositeKey>()
                                                                         .Include(x => x.LongTestValue)
                                                                         .Include(nameof(TestEntityCompositeKey.DateTimeTestValue));

            // Invoke the method and check that the result the updated expected entities
            IReadOnlyCollection <TestEntityCompositeKey> result = await context.UpdateAsync(
                expectedEntities,
                condition : x => x.Incoming.IntTestValue > x.Current.IntTestValue, // Only update if IntTestValue is greater than the incoming value, which rules out "Should not be updated 1"
                clusivityBuilder : inclusionBuilder);

            var expectedUpdatedEntity = new TestEntityCompositeKey
            {
                IdPartA           = expectedEntities[1].IdPartA,
                IdPartB           = expectedEntities[1].IdPartB,
                IntTestValue      = existingEntities[2].IntTestValue,  // We did not include this field in the update => it should have its original value
                BoolTestValue     = existingEntities[2].BoolTestValue, // We did not include this field in the update => it should have its original value
                DateTimeTestValue = expectedEntities[1].DateTimeTestValue,
                LongTestValue     = expectedEntities[1].LongTestValue,
            };

            result.Should().BeEquivalentTo(new[] { expectedUpdatedEntity });

            // Validate that the DB is updated
            context.TestEntitiesWithCompositeKey.Should().BeEquivalentTo(new[] { existingEntities[0], existingEntities[1], expectedUpdatedEntity });
        }
        public async Task UpdateAsync_ShouldReturnAffectedUpdatedCollection_WhenASubsetOfEntitiesAreMatchingWithConditionUsingTvpInterceptor()
        {
            TestInterceptorEntity[] existingEntities = Enumerable.Range(0, 52).Select(id => new TestInterceptorEntity
            {
                Id              = id.ToString(CultureInfo.InvariantCulture),
                IntTestValue    = id % 2,
                BoolTestValue   = false,
                StringTestValue = "short string",
            }).ToArray();
            TestInterceptorEntity[] expectedEntities = Enumerable.Range(0, 52).Select(id => new TestInterceptorEntity
            {
                Id            = id.ToString(CultureInfo.InvariantCulture),
                IntTestValue  = 1,
                BoolTestValue = true,
                // The string field has a max length of 25 chars set with an attribute.
                // We're extending the max length with the TvpInterceptor by changing the type it will have in the temporary table.
                StringTestValue = "a really long string which is longer than the limit we have on the property",
            }).ToArray();

            var interceptedProperties = new List <IInterceptedProperty>();

            TestTableValuedParameterInterceptor.TestCallback = (properties) => interceptedProperties.AddRange(properties);

            // We're only using Table Valued Parameters in SqlServer
            using TestDbContext context = await ContextFactory.GetDbContextAsync(DbProvider.SqlServer, seedData : existingEntities);

            // Make sure we're using TVP and add the test interceptor
            context.ManipulationExtensionsConfiguration.SqlServerConfiguration.AddEntityConifugration <TestInterceptorEntity>(
                new Configuration.EntityConifugration
            {
                UseTableValuedParametersParameterCountTreshold = 0,
                TableValuedParameterInterceptor = new TestTableValuedParameterInterceptor(),
            });

            // Include bool values - they are the only items expected to be updated based on the mocked data.
            InclusionBuilder <TestInterceptorEntity> inclusionBuilder = new InclusionBuilder <TestInterceptorEntity>().Include(x => x.BoolTestValue);

            // Invoke the method and check that the result the updated expected entities
            IReadOnlyCollection <TestInterceptorEntity> result = await context.UpdateAsync(
                expectedEntities,
                condition : x => x.Incoming.IntTestValue == x.Current.IntTestValue, // Only update if IntTestValue is equal to the incoming value
                clusivityBuilder : inclusionBuilder);

            Assert.AreEqual(expectedEntities.Length / 2, result.Count);
            Assert.IsTrue(result.All(r => r.BoolTestValue));

            // Validate that the DB is updated
            context.TestInterceptorEntities.Should().BeEquivalentTo(existingEntities.Select(e => new TestInterceptorEntity
            {
                Id              = e.Id,
                IntTestValue    = e.IntTestValue,
                BoolTestValue   = e.IntTestValue == 1,
                StringTestValue = e.StringTestValue,
            }));

            // Validate the TvpInterceptor has been called and what it returned
            foreach (KeyValuePair <string, string> propertyKvp in TestTableValuedParameterInterceptor.PropertyTypeOverrides)
            {
                Assert.AreEqual(1, interceptedProperties.Count(p => p.ColumnName == propertyKvp.Key && p.ColumnType == propertyKvp.Value));
            }
            Assert.AreEqual(typeof(TestInterceptorEntity).GetProperties().Length, interceptedProperties.Count);
        }