using DataModel.Database; using DataModel.Database.Common; using DataModel.MigrationScripts; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace DataModel; public static class Setup { private const string ENV_EF_TOOLING_DATABASE = "ENV_EF_TOOLING_DATABASE"; private const string DB_READ_WRITE_MODE = "ReadWrite"; private const string DB_READ_WRITE_CREATE_MODE = "ReadWriteCreate"; private static string USED_DATA_FILE = string.Empty; private static SetupMaintenance SETUP_MAINTENANCE = new(); public static string DataFile => Setup.USED_DATA_FILE; public static SetupMaintenance Maintenance => Setup.SETUP_MAINTENANCE; /// /// Tries to migrate the database. /// public static async Task PerformDataMigration(DataContext dbContext) { var pendingMigrations = (await dbContext.Database.GetPendingMigrationsAsync()).ToList(); foreach (var pendingMigration in pendingMigrations) { Console.WriteLine($"The migration '{pendingMigration}' is pending."); } await dbContext.Database.MigrateAsync(); // // Post migration actions: // if (pendingMigrations.Contains("20221106193544_202211AddUniqueIds")) await Script202211AddUniqueIds.PostMigrationAsync(dbContext); } /// /// Imports a JSON file into a new database. /// public static async Task ImportDataAndAddDatabase(this IServiceCollection serviceCollection, string path2JSONFile) { var tempPath = Path.GetTempFileName(); Setup.USED_DATA_FILE = tempPath; Setup.SETUP_MAINTENANCE = new(path2JSONFile, true); serviceCollection.AddDbContext(options => options.UseSqlite($"Filename={tempPath};Mode={DB_READ_WRITE_CREATE_MODE};"), ServiceLifetime.Transient); // Get the database service: await using var serviceProvider = serviceCollection.BuildServiceProvider(); var dbContext = serviceProvider.GetRequiredService(); // Next, we import the data from the provided JSON file: await dbContext.ImportAsync(path2JSONFile); // // Next, we enable the auto-export feature to keep the source file up to date. // The auto-export feature might exist, but we enforce it, when we work with a // temporary database source by a JSON file. // // Enable the auto-export feature: var autoExportEnabled = await dbContext.Settings.FirstAsync(n => n.Code == SettingNames.AUTO_EXPORT_ENABLED); autoExportEnabled.BoolValue = true; // Set the auto-export path and file: var autoExportPath = await dbContext.Settings.FirstAsync(n => n.Code == SettingNames.AUTO_EXPORT_DESTINATION_PATH); autoExportPath.TextValue = Path.GetDirectoryName(path2JSONFile) ?? string.Empty; var autoExportFile = await dbContext.Settings.FirstAsync(n => n.Code == SettingNames.AUTO_EXPORT_FILENAME); autoExportFile.TextValue = Path.GetFileName(path2JSONFile); // Save the changes: await dbContext.SaveChangesAsync(); } /// /// Creates and adds the database instance to the DI system (extension method). /// public static void AddDatabase(this IServiceCollection serviceCollection, string path2DataFile, bool createWhenNecessary = true) { Setup.USED_DATA_FILE = path2DataFile; Setup.SETUP_MAINTENANCE = new(path2DataFile, false); serviceCollection.AddDbContext(options => options.UseSqlite($"Filename={path2DataFile};Mode={(createWhenNecessary ? DB_READ_WRITE_CREATE_MODE : DB_READ_WRITE_MODE)};"), ServiceLifetime.Transient); } /// /// Sets up the DI & db context ready for the EF tooling. /// public static IHostBuilder Setup4EFTooling(string[] args) { var dataFile = Environment.GetEnvironmentVariable(ENV_EF_TOOLING_DATABASE); if (string.IsNullOrWhiteSpace(dataFile)) { Console.WriteLine("In order to use EF tooling, point the environment variable ENV_EF_TOOLING_DATABASE to the data file, which should be used for the EF tooling."); Environment.Exit(100); } var builder = Host.CreateDefaultBuilder(args); builder.ConfigureServices((hostContext, serviceCollection) => { serviceCollection.AddDbContext(options => options.UseSqlite($"Filename={dataFile};Mode=ReadWriteCreate")); }); return builder; } public readonly record struct SetupMaintenance(string PathToDataFile = "", bool RemoveTempDatabaseAfterwards = false) : IDisposable { public void Dispose() { if (!this.RemoveTempDatabaseAfterwards) return; try { File.Delete(this.PathToDataFile); } catch(Exception e) { Console.WriteLine($"Failed to remove the temporary database file: {e.Message} // {e.InnerException?.Message}"); } } } }