using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; using Microsoft.Extensions.Configuration; using OptixServe.Infrastructure.Configuration; using OptixServe.Infrastructure.Data; namespace OptixServe.Infrastructure.Utilites; public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory { private record CommandParseResult { public string? ConfigPath { get; set; } = null; public string? DataDir { get; set; } = null; } private static CommandParseResult ParseArgs(string[] args) { var result = new CommandParseResult(); bool configSet = false; bool dataDirSet = false; for (int i = 0; i < args.Length; i++) { string arg = args[i]; if (arg == "--config" || arg == "-c") { if (configSet) { throw new ArgumentException("The --config/-c option can only be specified once."); } if (i + 1 >= args.Length) { throw new ArgumentException("Missing value for --config/-c option."); } result.ConfigPath = args[i + 1]; configSet = true; i++; } else if (arg == "--data-dir" || arg == "-d") { if (dataDirSet) { throw new ArgumentException("The --data-dir/-d option can only be specified once."); } if (i + 1 >= args.Length) { throw new ArgumentException("Missing value for --data-dir/-d option."); } result.DataDir = args[i + 1]; dataDirSet = true; i++; } else { throw new ArgumentException($"Unknown argument: {arg}"); } } return result; } /// /// Creates DbContext instance in Design-Time /// /// Custom arguments should be passed in dotnet command /// /// Example:
/// dotnet ef database update -- -c ../data/appsettings.Development.json -d ../data/` /// ///
/// /// /// /// --config/-c: App configuration file to load with database connection settings. /// /// /// --data-dir/-d: App data dir to work with. /// Currently this only affects finding SQLite database when relative path /// is specified in database settings. /// /// /// /// public AppDbContext CreateDbContext(string[] args) { var parsedArgs = ParseArgs(args); IConfigurationRoot configuration; if (!string.IsNullOrEmpty(parsedArgs.ConfigPath)) { var absConfigPath = Path.GetFullPath(parsedArgs.ConfigPath); var configDirectory = Path.GetDirectoryName(absConfigPath); var configFileName = Path.GetFileName(absConfigPath); // Use the provided config file path configuration = new ConfigurationBuilder() .SetBasePath(configDirectory ?? "") .AddJsonFile(configFileName, optional: false) .Build(); } else { // Default search configuration path configuration = ConfigurationHelper.CreateDefaultBuilder().Build(); } var dbSettings = configuration.GetSection("OptixServe:Infrastructure:Database").Get()!; // Resolve SQLite database if `--data-dir` is specified and `dbSettings.Host` is relative if (parsedArgs.DataDir != null) { if (dbSettings.Type == DatabaseType.Sqlite && !Path.IsPathFullyQualified(dbSettings.Host!)) { var dbRelPath = Path.Combine(parsedArgs.DataDir, dbSettings.Host!); var dbAbsPath = Path.GetFullPath(dbRelPath); dbSettings.Host = dbAbsPath; } } var optionsBuilder = new DbContextOptionsBuilder(); DatabaseHelper.ConfigureDbContext(optionsBuilder, dbSettings); return new AppDbContext(optionsBuilder.Options); } }