c# - 选择要在启动时自动迁移的 DbContext
问题描述
我正在为 .NET Core 应用程序开发一个小型可重用包,这将有助于在应用程序启动时自动迁移。
它基本上会Database.Migrate()
在每个DbContext
.
但这里的问题是我只想在已“标记”为自动迁移的 DbContexts 上运行它。我在想我可以扩展AddDbContext
,并以某种方式告诉IServiceCollection
跟踪特定的DbContext
. 像这样的东西:
public static IServiceCollection AddDbContextWithMigration<TContext>(this IServiceCollection serviceCollection, Action<DbContextOptionsBuilder> optionsAction = null, ServiceLifetime contextLifetime = ServiceLifetime.Scoped, ServiceLifetime optionsLifetime = ServiceLifetime.Scoped) where TContext : DbContext
{
//TODO: Somehow remember that this DbContext should be migrated.
return serviceCollection.AddDbContext<TContext, TContext>(optionsAction, contextLifetime, optionsLifetime);
}
用法:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddDbContextWithMigration<DbContext1>();
services.AddDbContext<DbContext2>();
services.AddDbContextWithMigration<DbContext3>();
}
然后我想我可以使用IStartupFilter
或创建一个扩展方法IApplicationBuilder
。
使用扩展方法:
public static IApplicationBuilder RunMigrations(this IApplicationBuilder app)
{
if (app == null)
throw new ArgumentNullException(nameof(app));
var contexts = app.ApplicationServices.GetService();
foreach (DbContext context in contexts)
{
context.Database.Migrate();
}
return app;
}
使用 IStartupFilter:
public class MigrationsFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return builder =>
{
var contexts = builder.ApplicationServices.GetService();
foreach (DbContext context in contexts)
{
context.Database.Migrate();
}
next(builder);
};
}
}
所以我基本上有两个问题。
- 如何跟踪应迁移的 DbContexts?
- 这是 的“正确”用法
IStartupFilter
吗?
解决方案
IContextMigrator
我通过为我的迁移注册一个包装类 ( ) 来解决它。
首先,我的扩展方法:
public static IApplicationBuilder RunMigrations(this IApplicationBuilder app)
{
if (app == null)
throw new ArgumentNullException(nameof(app));
IEnumerable<IContextMigrator> migrations = app.ApplicationServices.GetServices<IContextMigrator>();
foreach (IContextMigrator migration in migrations)
{
migration.Migrate();
// Other logic for dispose...
}
return app;
}
public static IServiceCollection AddDbContextWithMigration<TContext>(this IServiceCollection serviceCollection, Action<DbContextOptionsBuilder> optionsAction = null, ServiceLifetime contextLifetime = ServiceLifetime.Scoped, ServiceLifetime optionsLifetime = ServiceLifetime.Scoped) where TContext : DbContext
{
// By simply registering a transient of a wrapper-class I can resolve it later.
serviceCollection.AddTransient<IContextMigrator, ContextMigrator<TContext>>();
return serviceCollection.AddDbContext<TContext, TContext>(optionsAction, contextLifetime, optionsLifetime);
}
然后是我的实际迁移课程:
public interface IContextMigrator : IDisposable
{
void Migrate();
}
/// <summary>
/// Class responsible for migration of a DbContext.
/// Used by ApplicationBuilderExtensions to perform migrations.
/// </summary>
/// <typeparam name="T"></typeparam>
internal sealed class ContextMigrator<T> : IContextMigrator
where T : DbContext
{
private readonly T context;
public ContextMigrator(T context)
{
this.context = context;
}
public void Migrate()
{
try
{
this.context.Database.Migrate();
}
catch (Exception e)
{
throw new MigrationException(context.GetType().Name, e);
}
}
public void Dispose()
{
}
}
推荐阅读
- java - java.lang.NullPointerException:火力基地
- node.js - 无法在 bitbucket 管道中运行 npm 命令
- android - 声纳与蜥蜴集成以跟踪 android studio 中失败的代码复杂性
- css - 为线性渐变形状添加边框
- java - 如何在 TestNG 中合并两个数据提供者
- winapi - winapi - 检测编辑控件上的鼠标按钮事件
- python - 在 python 中打印数字金字塔模式
- python - Python ; Tkinter 光标边框
- amazon-ec2 - 如何允许实例访问负载均衡器中的端口
- python - 错误“时间”未定义:UndefinedVariable