diff --git a/DataDude.Tests/Inserts/AutoInsertFKTableTestscs.cs b/DataDude.Tests/Inserts/AutoInsertFKTableTestscs.cs index 2a94407..0a807f2 100644 --- a/DataDude.Tests/Inserts/AutoInsertFKTableTestscs.cs +++ b/DataDude.Tests/Inserts/AutoInsertFKTableTestscs.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using DataDude.Instructions.Insert; using DataDude.Instructions.Insert.AutomaticForeignKeys; @@ -113,5 +114,64 @@ public async Task Handles_Ignore_Nullable_Keys() .Select(x => x.TableName) .ShouldBe(new[] { "B" }); } + + [Fact] + public async Task Handles_Previously_Inserted_Rows() + { + var schema = new TestSchema(); + var a = schema.AddTable("A"); + var b = schema.AddTable("B").AddFk(a); + var c = schema.AddTable("C").AddFk(b); + + var context = new DataDudeContext(schema); + await context.LoadSchema(null, null); + + // Simulate that dude.Go(..) from a previous execution has been executed and a row has already been inserted into table A + InsertContext.Get(context).InsertedRows.Add(new InsertedRow(a, new Dictionary(), null)); + + context.Instructions.Add(new InsertInstruction("C")); + var dependencyService = new DependencyService(DependencyTraversalStrategy.FollowAllForeignKeys); + await new AddMissingInsertInstructionsPreProcessor(dependencyService).PreProcess(context); + context.Instructions + .OfType() + .Select(x => x.TableName) + .ShouldBe(new[] { "[dbo].[B]", "C" }); + } + + [Fact] + public void Can_Enable_AutoFks_Multiple_Times() + { + var dude = new Dude() + .EnableAutomaticForeignKeys(x => x.AddMissingForeignKeys = false) + .EnableAutomaticForeignKeys(x => x.AddMissingForeignKeys = true); + + dude.Configure(context => + { + context.InstructionPreProcessors.OfType().ShouldHaveSingleItem(); + }); + + dude.ConfigureInsert(insertContext => + { + insertContext.InsertInterceptors.OfType().ShouldHaveSingleItem(); + }); + } + + [Fact] + public void Can_Disable_AutoFks() + { + var dude = new Dude() + .EnableAutomaticForeignKeys(x => x.AddMissingForeignKeys = true) + .DisableAutomaticForeignKeys(); + + dude.Configure(context => + { + context.InstructionPreProcessors.OfType().ShouldBeEmpty(); + }); + + dude.ConfigureInsert(insertContext => + { + insertContext.InsertInterceptors.OfType().ShouldBeEmpty(); + }); + } } } diff --git a/DataDude.Tests/Inserts/InstructionTests.cs b/DataDude.Tests/Inserts/InstructionTests.cs index a679a95..8083968 100644 --- a/DataDude.Tests/Inserts/InstructionTests.cs +++ b/DataDude.Tests/Inserts/InstructionTests.cs @@ -144,6 +144,12 @@ public async Task Can_Insert_With_Automatic_Foreign_Keys_And_Add_Missing_Insert_ .Insert("OfficeOccupant") .Go(connection); + var offices = await connection.QueryAsync("SELECT * FROM Buildings.Office"); + offices.ShouldHaveSingleItem(); + + var employees = await connection.QueryAsync("SELECT * FROM People.Employee"); + offices.ShouldHaveSingleItem(); + var occupants = await connection.QueryAsync("SELECT * FROM Buildings.OfficeOccupant"); occupants.ShouldHaveSingleItem(); } @@ -312,5 +318,32 @@ await dude insertedOffices.ShouldBe(1); insertedEmployees.ShouldBe(1); } + + [Fact] + public async Task Split_Inserts_Keeps_Track_Of_Previously_Inserted_Data() + { + using var connection = Fixture.CreateNewConnection(); + + var dude = new Dude() + .EnableAutomaticForeignKeys(x => x.AddMissingForeignKeys = true); + + // Inserts Office, Employee, OfficeOccupant and OfficeOccupancy + await dude.Insert("OfficeOccupancy").Go(connection); + + // Should insert OfficeOccupancy only + await dude.Insert("OfficeOccupancy").Go(connection); + + var offices = await connection.QueryAsync("SELECT * FROM Buildings.Office"); + offices.ShouldHaveSingleItem(); + + var employees = await connection.QueryAsync("SELECT * FROM People.Employee"); + offices.ShouldHaveSingleItem(); + + var occupants = await connection.QueryAsync("SELECT * FROM Buildings.OfficeOccupant"); + occupants.ShouldHaveSingleItem(); + + var officeOccupancies = await connection.QueryAsync("SELECT * FROM People.OfficeOccupancy"); + officeOccupancies.ShouldContain(x => true, 2, "Should contain two occupancies"); + } } } diff --git a/DataDude/DataDude.csproj b/DataDude/DataDude.csproj index 77cd6a2..8ec34d7 100644 --- a/DataDude/DataDude.csproj +++ b/DataDude/DataDude.csproj @@ -4,7 +4,7 @@ netstandard2.0 9.0 enable - 0.7.0 + 0.7.1 true true snupkg diff --git a/DataDude/Instructions/Insert/AutomaticForeignKeys/AddMissingInsertInstructionsPreProcessor.cs b/DataDude/Instructions/Insert/AutomaticForeignKeys/AddMissingInsertInstructionsPreProcessor.cs index 164bbb9..5fcb0bf 100644 --- a/DataDude/Instructions/Insert/AutomaticForeignKeys/AddMissingInsertInstructionsPreProcessor.cs +++ b/DataDude/Instructions/Insert/AutomaticForeignKeys/AddMissingInsertInstructionsPreProcessor.cs @@ -21,8 +21,10 @@ public Task PreProcess(DataDudeContext context) { if (context.Schema?[instruction.TableName] is { } table) { + IEnumerable insertedRows = InsertContext.Get(context)?.InsertedRows ?? Array.Empty(); var dependencies = _dependencyService.GetOrderedDependenciesFor(table) .Where(t => !toInsert.Values.Any(x => x.Contains(t))) + .Where(t => !insertedRows.Any(x => x.Table == t)) .ToList(); toInsert.Add(instruction, new InsertInformation(table, dependencies)); diff --git a/DataDude/Instructions/Insert/InsertExtensions.cs b/DataDude/Instructions/Insert/InsertExtensions.cs index 11718e9..6a9df1b 100644 --- a/DataDude/Instructions/Insert/InsertExtensions.cs +++ b/DataDude/Instructions/Insert/InsertExtensions.cs @@ -31,8 +31,13 @@ public static Dude EnableAutomaticForeignKeys(this Dude dude, Action x.InsertInterceptors.Insert(0, new ForeignKeyInterceptor())); + dude.ConfigureInsert(insertContext => + { + insertContext.InsertInterceptors.Insert(0, new ForeignKeyInterceptor()); + }); if (config.AddMissingForeignKeys) { @@ -43,6 +48,31 @@ public static Dude EnableAutomaticForeignKeys(this Dude dude, Action + { + var existingInterceptors = insertContext.InsertInterceptors.OfType().ToList(); + foreach (var existingFKInterceptors in existingInterceptors) + { + insertContext.InsertInterceptors.Remove(existingFKInterceptors); + } + }); + + // Clean out existing AddMissingInsertInstructionsPreProcessors + dude.Configure(x => + { + var existingPreProcessors = x.InstructionPreProcessors.OfType().ToList(); + foreach (var existingPreProcessor in existingPreProcessors) + { + x.InstructionPreProcessors.Remove(existingPreProcessor); + } + }); + + return dude; + } + public static Dude ConfigureCustomColumnValue(this Dude dude, Action getValue) { dude.ConfigureInsert(x => x.InsertValueProviders.Insert(0, new CustomValueProvider(getValue)));