public async Task BatchCommands_creates_valid_batch_for_added_entities()
        {
            var stateEntry = new MixedStateEntry(
                CreateConfiguration(),
                CreateSimpleFKModel().GetEntityType(typeof(FakeEntity)), new FakeEntity { Id = 42, Value = "Test" });

            await stateEntry.SetEntityStateAsync(EntityState.Added);

            var commandBatches = CreateCommandBatchPreparer().BatchCommands(new[] { stateEntry }).ToArray();
            Assert.Equal(1, commandBatches.Count());
            Assert.Equal(1, commandBatches.First().ModificationCommands.Count());

            var command = commandBatches.First().ModificationCommands.Single();
            Assert.Equal(EntityState.Added, command.EntityState);
            Assert.Equal(2, command.ColumnModifications.Count);

            var columnMod = command.ColumnModifications[0];

            Assert.Equal("Id", columnMod.ColumnName);
            Assert.Same(stateEntry, columnMod.StateEntry);
            Assert.Equal("Id", columnMod.Property.Name);
            Assert.False(columnMod.IsCondition);
            Assert.True(columnMod.IsKey);
            Assert.False(columnMod.IsRead);
            Assert.True(columnMod.IsWrite);

            columnMod = command.ColumnModifications[1];

            Assert.Equal("Value", columnMod.ColumnName);
            Assert.Same(stateEntry, columnMod.StateEntry);
            Assert.Equal("Value", columnMod.Property.Name);
            Assert.False(columnMod.IsCondition);
            Assert.False(columnMod.IsKey);
            Assert.False(columnMod.IsRead);
            Assert.True(columnMod.IsWrite);
        }
        public async Task BatchCommands_creates_batches_lazily()
        {
            var configuration = CreateConfiguration();
            var model = CreateSimpleFKModel();

            var fakeEntity = new FakeEntity { Id = 42, Value = "Test" };
            var stateEntry = new MixedStateEntry(
                configuration,
                model.GetEntityType(typeof(FakeEntity)), fakeEntity);
            await stateEntry.SetEntityStateAsync(EntityState.Added);

            var relatedStateEntry = new MixedStateEntry(
                configuration,
                model.GetEntityType(typeof(RelatedFakeEntity)), new RelatedFakeEntity { Id = 42 });
            await relatedStateEntry.SetEntityStateAsync(EntityState.Added);

            var modificationCommandBatchFactoryMock = new Mock<ModificationCommandBatchFactory>();

            var commandBatches = CreateCommandBatchPreparer(modificationCommandBatchFactoryMock.Object).BatchCommands(new[] { relatedStateEntry, stateEntry });

            var commandBatchesEnumerator = commandBatches.GetEnumerator();
            commandBatchesEnumerator.MoveNext();

            modificationCommandBatchFactoryMock.Verify(mcb => mcb.Create(), Times.Once);

            commandBatchesEnumerator.MoveNext();

            modificationCommandBatchFactoryMock.Verify(mcb => mcb.Create(), Times.Exactly(2));
        }
        public async Task BatchCommands_sorts_entities_when_reparenting()
        {
            var configuration = CreateConfiguration();
            var model = CreateCyclicFKModel();

            var previousParent = new MixedStateEntry(
                configuration,
                model.GetEntityType(typeof(FakeEntity)), new FakeEntity { Id = 42, Value = "Test" });
            await previousParent.SetEntityStateAsync(EntityState.Deleted);

            var newParent = new MixedStateEntry(
                configuration,
                model.GetEntityType(typeof(FakeEntity)), new FakeEntity { Id = 3, Value = "Test" });
            await newParent.SetEntityStateAsync(EntityState.Added);

            var relatedStateEntry = new MixedStateEntry(
                configuration,
                model.GetEntityType(typeof(RelatedFakeEntity)), new RelatedFakeEntity { Id = 1, RelatedId = 3 });
            await relatedStateEntry.SetEntityStateAsync(EntityState.Modified);
            relatedStateEntry.OriginalValues[relatedStateEntry.EntityType.GetProperty("RelatedId")] = 42;
            relatedStateEntry.SetPropertyModified(relatedStateEntry.EntityType.GetKey().Properties.Single(), isModified: false);

            var commandBatches = CreateCommandBatchPreparer().BatchCommands(new[] { relatedStateEntry, previousParent, newParent }).ToArray();

            Assert.Equal(
                new[] { newParent, relatedStateEntry, previousParent },
                commandBatches.Select(cb => cb.ModificationCommands.Single()).Select(mc => mc.StateEntries.Single()));
        }
        public async Task BatchCommands_sorts_unrelated_entities()
        {
            var configuration = CreateConfiguration();
            var model = CreateSimpleFKModel();

            var firstStateEntry = new MixedStateEntry(
                configuration,
                model.GetEntityType(typeof(FakeEntity)), new FakeEntity { Id = 42, Value = "Test" });
            await firstStateEntry.SetEntityStateAsync(EntityState.Added);

            var secondStateEntry = new MixedStateEntry(
                configuration,
                model.GetEntityType(typeof(RelatedFakeEntity)), new RelatedFakeEntity { Id = 1 });
            await secondStateEntry.SetEntityStateAsync(EntityState.Added);

            var commandBatches = CreateCommandBatchPreparer().BatchCommands(new[] { secondStateEntry, firstStateEntry }).ToArray();

            Assert.Equal(
                new[] { firstStateEntry, secondStateEntry },
                commandBatches.Select(cb => cb.ModificationCommands.Single()).Select(mc => mc.StateEntries.Single()));
        }
        public async Task BatchCommands_throws_on_modified_principal_key()
        {
            var configuration = CreateConfiguration();
            var model = CreateSimpleFKModel();

            var stateEntry = new MixedStateEntry(
                configuration,
                model.GetEntityType(typeof(FakeEntity)), new FakeEntity { Id = 42, Value = "Test" });
            await stateEntry.SetEntityStateAsync(EntityState.Modified);
            stateEntry.SetPropertyModified(stateEntry.EntityType.GetKey().Properties.Single(), isModified: true);

            var relatedStateEntry = new MixedStateEntry(
                configuration,
                model.GetEntityType(typeof(RelatedFakeEntity)), new RelatedFakeEntity { Id = 42 });
            await relatedStateEntry.SetEntityStateAsync(EntityState.Modified);

            Assert.Equal(
                Strings.FormatPrincipalKeyModified(),
                Assert.Throws<InvalidOperationException>(() => CreateCommandBatchPreparer().BatchCommands(new[] { relatedStateEntry, stateEntry })).Message);
        }
        public void Compare_returns_0_only_for_commands_that_are_equal()
        {
            var mCC = new ModificationCommandComparer();

            var configuration = new DbContext(new DbContextOptions().UseInMemoryStore(persist: false)).Configuration;

            var entityType1 = new EntityType(typeof(object));
            var key1 = entityType1.AddProperty("Id", typeof(int), shadowProperty: true, concurrencyToken: false);
            entityType1.SetKey(key1);
            var stateEntry1 = new MixedStateEntry(configuration, entityType1, new object());
            stateEntry1[key1] = 0;
            stateEntry1.EntityState = EntityState.Added;
            var modificationCommandAdded = new ModificationCommand("A", null, new ParameterNameGenerator());
            modificationCommandAdded.AddStateEntry(stateEntry1);

            var entityType2 = new EntityType(typeof(object));
            var key2 = entityType2.AddProperty("Id", typeof(int), shadowProperty: true, concurrencyToken: false);
            entityType2.SetKey(key2);
            var stateEntry2 = new MixedStateEntry(configuration, entityType2, new object());
            stateEntry2[key2] = 0;
            stateEntry2.EntityState = EntityState.Modified;
            var modificationCommandModified = new ModificationCommand("A", null, new ParameterNameGenerator());
            modificationCommandModified.AddStateEntry(stateEntry2);

            var entityType3 = new EntityType(typeof(object));
            var key3 = entityType3.AddProperty("Id", typeof(int), shadowProperty: true, concurrencyToken: false);
            entityType3.SetKey(key3);
            var stateEntry3 = new MixedStateEntry(configuration, entityType3, new object());
            stateEntry3[key3] = 0;
            stateEntry3.EntityState = EntityState.Deleted;
            var modificationCommandDeleted = new ModificationCommand("A", null, new ParameterNameGenerator());
            modificationCommandDeleted.AddStateEntry(stateEntry3);

            Assert.True(0 == mCC.Compare(new ModificationCommand("A", null, new ParameterNameGenerator()), new ModificationCommand("A", null, new ParameterNameGenerator())));
            Assert.True(0 == mCC.Compare(new ModificationCommand("A", "dbo", new ParameterNameGenerator()), new ModificationCommand("A", "dbo", new ParameterNameGenerator())));
            Assert.True(0 == mCC.Compare(null, null));

            Assert.True(0 > mCC.Compare(new ModificationCommand("A", null, new ParameterNameGenerator()), new ModificationCommand("A", "dbo", new ParameterNameGenerator())));
            Assert.True(0 < mCC.Compare(new ModificationCommand("A", "foo", new ParameterNameGenerator()), new ModificationCommand("A", "dbo", new ParameterNameGenerator())));

            Assert.True(0 > mCC.Compare(null, new ModificationCommand("A", null, new ParameterNameGenerator())));
            Assert.True(0 < mCC.Compare(new ModificationCommand("A", null, new ParameterNameGenerator()), null));

            Assert.True(0 > mCC.Compare(new ModificationCommand("A", null, new ParameterNameGenerator()), new ModificationCommand("B", null, new ParameterNameGenerator())));
            Assert.True(0 < mCC.Compare(new ModificationCommand("B", null, new ParameterNameGenerator()), new ModificationCommand("A", null, new ParameterNameGenerator())));

            Assert.True(0 > mCC.Compare(new ModificationCommand("A", "dbo", new ParameterNameGenerator()), new ModificationCommand("B", "dbo", new ParameterNameGenerator())));
            Assert.True(0 < mCC.Compare(new ModificationCommand("B", "dbo", new ParameterNameGenerator()), new ModificationCommand("A", "dbo", new ParameterNameGenerator())));

            Assert.True(0 > mCC.Compare(new ModificationCommand("A", "dbo", new ParameterNameGenerator()), new ModificationCommand("B", null, new ParameterNameGenerator())));
            Assert.True(0 < mCC.Compare(new ModificationCommand("B", "dbo", new ParameterNameGenerator()), new ModificationCommand("A", "foo", new ParameterNameGenerator())));

            Assert.True(0 > mCC.Compare(modificationCommandModified, modificationCommandAdded));
            Assert.True(0 < mCC.Compare(modificationCommandAdded, modificationCommandModified));

            Assert.True(0 > mCC.Compare(modificationCommandDeleted, modificationCommandAdded));
            Assert.True(0 < mCC.Compare(modificationCommandAdded, modificationCommandDeleted));

            Assert.True(0 > mCC.Compare(modificationCommandDeleted, modificationCommandModified));
            Assert.True(0 < mCC.Compare(modificationCommandModified, modificationCommandDeleted));
        }