应用所有 EF Core 迁移后运行代码

问题描述 投票:0回答:2
EF Core 5 有各种

事件,但它们与 DbContext

 相关。没有与迁移相关的事件 (
Migration
)。

我想在应用所有迁移后运行自定义代码 - 无论是由代码 (

context.Database.Migrate()

) 还是 CLI (
dotnet ef database update
) 触发。

解决方法是添加一个“空”迁移,并将我的代码放入其

Up

 方法中。但每次添加迁移时我都需要这样做。

假设没有我可以使用的事件或挂钩(有吗?),在应用所有迁移后如何运行自定义代码?

c# .net entity-framework entity-framework-core entity-framework-migrations
2个回答
3
投票
这是一个解决方法。

[DbContext(typeof(MyContext))] [Migration("99999999999999_Last1")] public class Last1 : Migration { protected override void Up(MigrationBuilder migrationBuilder) { Task.Run(() => callPostMigrationCodeThatIsIdempotent()).GetAwaiter().GetResult(); migrationBuilder.DeleteData(HistoryRepository.DefaultTableName, nameof(HistoryRow.MigrationId), "string", "99999999999999_Last2", null); } } [DbContext(typeof(MyContext))] [Migration("99999999999999_Last2")] public class Last2 : Migration { protected override void Up(MigrationBuilder migrationBuilder) { Task.Run(() => callPostMigrationCodeThatIsIdempotent()).GetAwaiter().GetResult(); migrationBuilder.DeleteData(HistoryRepository.DefaultTableName, nameof(HistoryRow.MigrationId), "string", "99999999999999_Last1", null); } }
有两次“最后”迁移:

    他们的 ID 是经过选择的,所以他们最后运行(“99999999999999”)
  • 两者都调用自定义代码,该代码
  • 必须是幂等的
  • 每个人都从历史表中删除另一个人
  • 他们使用
  • Task.Run
     进行异步而非同步
在每次运行期间,一个或另一个运行自定义代码,并从历史表中删除另一个。在下一次运行中,将会发生相反的情况。

相同的双重设置可用于预迁移挂钩。

repo 上有一个未完成的积压项目。


0
投票
这可以通过扩展标准 EFCore 迁移器实现来实现:

public class CustomMigrator : Migrator { public GenericMigrator( IMigrationsAssembly migrationsAssembly, IHistoryRepository historyRepository, IDatabaseCreator databaseCreator, IMigrationsSqlGenerator migrationsSqlGenerator, IRawSqlCommandBuilder rawSqlCommandBuilder, IMigrationCommandExecutor migrationCommandExecutor, IRelationalConnection connection, ISqlGenerationHelper sqlGenerationHelper, ICurrentDbContext currentContext, IModelRuntimeInitializer modelRuntimeInitializer, IDiagnosticsLogger<DbLoggerCategory.Migrations> logger, IRelationalCommandDiagnosticsLogger commandLogger, IDatabaseProvider databaseProvider) : base(migrationsAssembly, historyRepository, databaseCreator, migrationsSqlGenerator, rawSqlCommandBuilder, migrationCommandExecutor, connection, sqlGenerationHelper, currentContext, modelRuntimeInitializer, logger, commandLogger, databaseProvider) { _connection = connection; } IRelationalConnection _connection; public override void Migrate(string? targetMigration = null) { base.Migrate(targetMigration); if (!_connection.DbConnection.State.HasFlag(ConnectionState.Open)) { _connection.Open(); } using var command = _connection.DbConnection.CreateCommand(); command.CommandText = "create or replace view v_aaa as select 1 as aaa"; command.ExecuteNonQuery(); } public override async Task MigrateAsync(string? targetMigration = null, CancellationToken cancellationToken = default) { await base.MigrateAsync(targetMigration, cancellationToken); if (!_connection.DbConnection.State.HasFlag(ConnectionState.Open)) { await _connection.OpenAsync(cancellationToken); } using var command = _connection.DbConnection.CreateCommand(); command.CommandText = "create or replace view v_aaa as select 1 as aaa"; await command.ExecuteNonQueryAsync(); } }
该服务应替换如下:

builder.Services.AddDbContext<TDbContext>(options => { options.UseNpgsql(builder.Configuration.GetConnectionString(connectionStringName)); options.ReplaceService<IMigrator, CustomMigrator>(); });
    
© www.soinside.com 2019 - 2024. All rights reserved.