BRAKING refactor project structure.
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.
This commit is contained in:
33
OptixServe.Infrastructure/Utilites/DatabaseHelper.cs
Normal file
33
OptixServe.Infrastructure/Utilites/DatabaseHelper.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OptixServe.Infrastructure.Configuration;
|
||||
|
||||
namespace OptixServe.Infrastructure.Utilites;
|
||||
|
||||
public static class DatabaseHelper
|
||||
{
|
||||
public static string BuildConnectionString(DatabaseSettings dbSettings)
|
||||
{
|
||||
return dbSettings.Type switch
|
||||
{
|
||||
DatabaseType.Sqlite => $"Data Source={dbSettings.Host ?? "optixserve.db"}",
|
||||
DatabaseType.MySQL => throw new NotSupportedException("MySQL connection is not yet implemented"),
|
||||
_ => throw new NotSupportedException($"Database type {dbSettings.Type} is not supported")
|
||||
};
|
||||
}
|
||||
|
||||
public static void ConfigureDbContext(DbContextOptionsBuilder options, DatabaseSettings dbSettings)
|
||||
{
|
||||
if (dbSettings?.Type == DatabaseType.Sqlite)
|
||||
{
|
||||
var dbPath = dbSettings.Host ?? "optixserve.db";
|
||||
var connectionString = $"Data Source={dbPath}";
|
||||
|
||||
options.UseSqlite(connectionString, b => b.MigrationsAssembly("OptixServe.Infrastructure"));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException("Only SQLite database is currently supported");
|
||||
}
|
||||
}
|
||||
}
|
129
OptixServe.Infrastructure/Utilites/DesignTimeDbContextFactory.cs
Normal file
129
OptixServe.Infrastructure/Utilites/DesignTimeDbContextFactory.cs
Normal file
@ -0,0 +1,129 @@
|
||||
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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user