Refactor: the project is now divided into a more clear structure, with **Infrastructure** and **Application** layers added. Refactor: configurations are split into sections for different layers. Fix: now EF Core related operations, such as migration, should be invoked in `OptixServe.Infrastructure`, with config file and data dir passed into `dotnet ef` command. See `OptixServe.Infrastructure/Utilites/DesignTimeDbContextFactory.cs` for details. Fix: EF migrations are ignored in gitignore on purpose in early development.
130 lines
4.4 KiB
C#
130 lines
4.4 KiB
C#
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<AppDbContext>
|
|
{
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates DbContext instance in Design-Time
|
|
///
|
|
/// <para>Custom arguments should be passed in <i>dotnet</i> command </para>
|
|
///
|
|
/// <b>Example:</b><br/>
|
|
/// <example><c>dotnet ef database update -- -c ../data/appsettings.Development.json -d ../data/`</c></example>
|
|
///
|
|
/// </summary>
|
|
/// <param name="args">
|
|
/// <list type="bullet">
|
|
/// <item>
|
|
/// --config/-c: App configuration file to load with database connection settings.
|
|
/// </item>
|
|
/// <item>
|
|
/// --data-dir/-d: App data dir to work with.
|
|
/// Currently this only affects finding SQLite database when relative path
|
|
/// is specified in database settings.
|
|
/// </item>
|
|
/// </list>
|
|
/// </param>
|
|
/// <returns></returns>
|
|
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<DatabaseSettings>()!;
|
|
|
|
// 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<AppDbContext>();
|
|
DatabaseHelper.ConfigureDbContext(optionsBuilder, dbSettings);
|
|
|
|
return new AppDbContext(optionsBuilder.Options);
|
|
}
|
|
}
|