From e4013bdd20ce9dcbdb665120347ae072b4adbcbe Mon Sep 17 00:00:00 2001 From: Eric Sondergard <4650644+esond@users.noreply.github.com> Date: Mon, 26 Feb 2024 11:30:37 -0600 Subject: [PATCH] Reset database using Respawn in DB setup tool --- build/Build.Deploy.cs | 26 +++++++++ build/Build.Test.cs | 28 ---------- tools/Setup/DbSetupCommand.cs | 77 +++++++------------------- tools/Setup/Spenses.Tools.Setup.csproj | 2 +- 4 files changed, 47 insertions(+), 86 deletions(-) diff --git a/build/Build.Deploy.cs b/build/Build.Deploy.cs index d9f336f..9f7ae3f 100644 --- a/build/Build.Deploy.cs +++ b/build/Build.Deploy.cs @@ -4,7 +4,9 @@ using Nuke.Common.ProjectModel; using Nuke.Common.Tooling; using Nuke.Common.Tools.DotNet; +using Nuke.Common.Tools.EntityFramework; using static Nuke.Common.Tools.DotNet.DotNetTasks; +using static Nuke.Common.Tools.EntityFramework.EntityFrameworkTasks; // ReSharper disable InconsistentNaming @@ -20,6 +22,12 @@ partial class Build Project WebProject => Solution.GetAllProjects("Spenses.App").Single(); + Target RestoreTools => t => t + .Executes(() => + { + DotNetToolRestore(s => s); + }); + Target Publish => t => t .Description("Publish deployment artifacts.") .Executes(() => @@ -31,6 +39,24 @@ partial class Build .AddProperty("PublishProfile", "DefaultContainer") .AddProperty("ContainerImageTags", GitVersion.NuGetVersionV2)); }); + Project RelationalSetupTool => Solution.GetAllProjects("Spenses.Tools.Setup").Single(); + + Target MigrateDatabase => t => t + .Description("Migrate the SQL Server database to the latest version.") + .DependsOn(RestoreTools) + .Requires(() => SqlServerConnectionString) + .Executes(() => + { + var dbContextProject = Solution.GetAllProjects("Spenses.Resources.Relational").Single(); + + EntityFrameworkDatabaseUpdate(s => s + .SetProject(dbContextProject) + .SetConnection(SqlServerConnectionString)); + + DotNetRun(s => s + .SetProjectFile(RelationalSetupTool) + .SetApplicationArguments($"views --connection \"{SqlServerConnectionString}\"")); + }); Target AzureLogin => t => t .Description("Log in with the Azure CLI.") diff --git a/build/Build.Test.cs b/build/Build.Test.cs index 6682595..643bbb5 100644 --- a/build/Build.Test.cs +++ b/build/Build.Test.cs @@ -1,14 +1,11 @@ using System.Collections.Generic; -using System.Linq; using Hexagrams.Nuke.Components; using Nuke.Common; using Nuke.Common.ProjectModel; using Nuke.Common.Tooling; using Nuke.Common.Tools.Coverlet; using Nuke.Common.Tools.DotNet; -using Nuke.Common.Tools.EntityFramework; using static Nuke.Common.Tools.DotNet.DotNetTasks; -using static Nuke.Common.Tools.EntityFramework.EntityFrameworkTasks; partial class Build { @@ -19,31 +16,6 @@ partial class Build public Configure TestSettings => s => s .SetExcludeByFile("\\\"*.Generated.cs,**/Resources/Relational/**.cs\\\""); - Target RestoreTools => t => t - .Executes(() => - { - DotNetToolRestore(s => s); - }); - - Project RelationalSetupTool => Solution.GetAllProjects("Spenses.Tools.Setup").Single(); - - Target MigrateDatabase => t => t - .Description("Migrate the SQL Server database to the latest version.") - .DependsOn(RestoreTools) - .Requires(() => SqlServerConnectionString) - .Executes(() => - { - var dbContextProject = Solution.GetAllProjects("Spenses.Resources.Relational").Single(); - - EntityFrameworkDatabaseUpdate(s => s - .SetProject(dbContextProject) - .SetConnection(SqlServerConnectionString)); - - DotNetRun(s => s - .SetProjectFile(RelationalSetupTool) - .SetApplicationArguments($"views --connection \"{SqlServerConnectionString}\"")); - }); - IEnumerable IntegrationTestProjects => Solution.GetAllProjects("*.IntegrationTests"); Target IntegrationTestSetup => t => t diff --git a/tools/Setup/DbSetupCommand.cs b/tools/Setup/DbSetupCommand.cs index 55a0cfa..eb4d9ee 100644 --- a/tools/Setup/DbSetupCommand.cs +++ b/tools/Setup/DbSetupCommand.cs @@ -1,10 +1,7 @@ using System.CommandLine; -using System.Data.Common; -using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; -using Microsoft.SqlServer.Management.Common; -using Microsoft.SqlServer.Management.Smo; +using Respawn; using Spenses.Resources.Relational; using Spenses.Resources.Relational.Infrastructure; using Spenses.Tools.Setup.SeedData; @@ -71,14 +68,29 @@ private Command GetResetDatabaseCommand() resetDatabaseCommand.SetHandler(async (connection, force, shouldSeed) => { - if (!await DropDatabase(connection, force)) + await using var dbContext = CreateDbContext(connection); + await using var dbConnection = dbContext.Database.GetDbConnection(); + + if (!force && !Confirm($"Really reset '{dbConnection.DataSource}/{dbConnection.Database}'?")) return; - await MigrateDatabase(connection); - await RebuildViews(connection); + await dbConnection.OpenAsync(); + + _logger.LogWarning("Resetting tables..."); + + var respawner = await Respawner.CreateAsync(dbConnection, new RespawnerOptions + { + CheckTemporalTables = true, + TablesToIgnore = ["__EFMigrationsHistory"] + }); + + await respawner.ResetAsync(dbConnection); + + await MigrateDatabase(dbConnection.ConnectionString); + await RebuildViews(dbConnection.ConnectionString); if (shouldSeed) - await SeedDatabase(connection); + await SeedDatabase(dbConnection.ConnectionString); _logger.LogInformation("Done resetting database!"); }, ConnectionOption, forceOption, seedOption); @@ -105,55 +117,6 @@ private async Task SeedDatabase(string connection) _logger.LogInformation("Database seeded."); } - public async Task DropDatabase(string connection, bool force) - { - await using var dbContext = CreateDbContext(connection); - await using var dbConnection = dbContext.Database.GetDbConnection(); - - if (!force && !Confirm($"Really drop '{dbConnection.DataSource}/{dbConnection.Database}'?")) - return false; - - var db = GetSqlServerDatabase(dbConnection); - - _logger.LogWarning("Dropping tables..."); - - var sql = string.Empty; - - foreach (Table? table in db.Tables) - { - sql = table!.ForeignKeys.Cast().Aggregate(sql, - (current, fk) => $"ALTER TABLE [{table.Name}] DROP CONSTRAINT [{fk!.Name}];\n" + current); - - sql += $"DROP TABLE [{table.Name}];\n"; - } - db.ExecuteNonQuery(sql); - - _logger.LogWarning("Dropping views..."); - - foreach (var view in db.Views.Cast().Where(v => !v.IsSystemObject)) - { - db.ExecuteNonQuery($"DROP VIEW [{view!.Name}]"); - } - - _logger.LogWarning("Dropping sequences..."); - - foreach (var sequence in db.Sequences.Cast()) - { - db.ExecuteNonQuery($"DROP SEQUENCE [{sequence!.Name}]"); - } - - return true; - } - - private static Database GetSqlServerDatabase(DbConnection dbConnection) - { - var server = new Server(new ServerConnection((SqlConnection) dbConnection)); - - server.Refresh(); - - return server.Databases[dbConnection.Database]; - } - private async Task MigrateDatabase(string connection) { await using var db = CreateDbContext(connection); diff --git a/tools/Setup/Spenses.Tools.Setup.csproj b/tools/Setup/Spenses.Tools.Setup.csproj index 0333d21..625098d 100644 --- a/tools/Setup/Spenses.Tools.Setup.csproj +++ b/tools/Setup/Spenses.Tools.Setup.csproj @@ -22,7 +22,7 @@ - +