事件,但它们与 DbContext
相关。没有与迁移相关的事件 (
Migration
)。我想在应用所有迁移后运行自定义代码 - 无论是由代码 (
context.Database.Migrate()
) 还是 CLI (
dotnet ef database update
) 触发。解决方法是添加一个“空”迁移,并将我的代码放入其
Up
方法中。但每次添加迁移时我都需要这样做。假设没有我可以使用的事件或挂钩(有吗?),在应用所有迁移后如何运行自定义代码?
[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);
}
}
有两次“最后”迁移:
Task.Run
进行异步而非同步
相同的双重设置可用于预迁移挂钩。
repo 上有一个未完成的积压项目。
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>();
});