自定义迁移操作Custom Migrations Operations

借助 MigrationBuilder API,你可以在迁移过程中执行多种不同的操作,但远远超出了这一过程。 不过,API 也可通过扩展来定义自己的操作。 可以通过两种方法来扩展 API:使用 Sql() 方法,或者通过定义自定义 MigrationOperation 对象。

为了说明这一点,让我们看一下如何实现使用每种方法创建数据库用户的操作。 在我们的迁移中,我们希望能够编写以下代码:

  1. migrationBuilder.CreateUser("SQLUser1", "Password");

使用 MigrationBuilder ()Using MigrationBuilder.Sql()

实现自定义操作的最简单方法是定义调用 MigrationBuilder.Sql()的扩展方法。 下面是生成相应 Transact-sql 的示例。

  1. static MigrationBuilder CreateUser(
  2. this MigrationBuilder migrationBuilder,
  3. string name,
  4. string password)
  5. => migrationBuilder.Sql($"CREATE USER {name} WITH PASSWORD '{password}';");

如果迁移需要支持多个数据库提供程序,则可以使用 “MigrationBuilder.ActiveProvider“ 属性。 下面是同时支持 Microsoft SQL Server 和 PostgreSQL 的示例。

  1. static MigrationBuilder CreateUser(
  2. this MigrationBuilder migrationBuilder,
  3. string name,
  4. string password)
  5. {
  6. switch (migrationBuilder.ActiveProvider)
  7. {
  8. case "Npgsql.EntityFrameworkCore.PostgreSQL":
  9. return migrationBuilder
  10. .Sql($"CREATE USER {name} WITH PASSWORD '{password}';");
  11. case "Microsoft.EntityFrameworkCore.SqlServer":
  12. return migrationBuilder
  13. .Sql($"CREATE USER {name} WITH PASSWORD = '{password}';");
  14. }
  15. return migrationBuilder;
  16. }

此方法仅在知道将应用自定义操作的每个提供程序时才有效。

使用 MigrationOperationUsing a MigrationOperation

若要将自定义操作与 SQL 分离,可以定义自己的 MigrationOperation 来表示它。 然后,将操作传递给提供程序,以便它可以确定要生成的相应 SQL。

  1. class CreateUserOperation : MigrationOperation
  2. {
  3. public string Name { get; set; }
  4. public string Password { get; set; }
  5. }

利用此方法,扩展方法只需将其中一个操作添加到 MigrationBuilder.Operations

  1. static MigrationBuilder CreateUser(
  2. this MigrationBuilder migrationBuilder,
  3. string name,
  4. string password)
  5. {
  6. migrationBuilder.Operations.Add(
  7. new CreateUserOperation
  8. {
  9. Name = name,
  10. Password = password
  11. });
  12. return migrationBuilder;
  13. }

此方法要求每个提供程序都知道如何在 IMigrationsSqlGenerator 服务中为此操作生成 SQL。 下面是一个示例,用于重写 SQL Server 的生成器来处理新操作。

  1. class MyMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator
  2. {
  3. public MyMigrationsSqlGenerator(
  4. MigrationsSqlGeneratorDependencies dependencies,
  5. IMigrationsAnnotationProvider migrationsAnnotations)
  6. : base(dependencies, migrationsAnnotations)
  7. {
  8. }
  9. protected override void Generate(
  10. MigrationOperation operation,
  11. IModel model,
  12. MigrationCommandListBuilder builder)
  13. {
  14. if (operation is CreateUserOperation createUserOperation)
  15. {
  16. Generate(createUserOperation, builder);
  17. }
  18. else
  19. {
  20. base.Generate(operation, model, builder);
  21. }
  22. }
  23. private void Generate(
  24. CreateUserOperation operation,
  25. MigrationCommandListBuilder builder)
  26. {
  27. var sqlHelper = Dependencies.SqlGenerationHelper;
  28. var stringMapping = Dependencies.TypeMappingSource.FindMapping(typeof(string));
  29. builder
  30. .Append("CREATE USER ")
  31. .Append(sqlHelper.DelimitIdentifier(operation.Name))
  32. .Append(" WITH PASSWORD = ")
  33. .Append(stringMapping.GenerateSqlLiteral(operation.Password))
  34. .AppendLine(sqlHelper.StatementTerminator)
  35. .EndCommand();
  36. }
  37. }

将默认迁移 sql 生成器服务替换为已更新的。

  1. protected override void OnConfiguring(DbContextOptionsBuilder options)
  2. => options
  3. .UseSqlServer(connectionString)
  4. .ReplaceService<IMigrationsSqlGenerator, MyMigrationsSqlGenerator>();