カスタムの移行操作Custom Migrations Operations

MigrationBuilder API では、移行中にさまざまな種類の操作を実行することができますが、排他的なかけ離れていること。The MigrationBuilder API allows you to perform many different kinds of operations during a migration, but it's far from exhaustive. ただし、また API は、拡張を独自の操作を定義できるようにします。However, the API is also extensible allowing you to define your own operations. API を拡張する 2 つの方法があります: を使用して、Sql()メソッド、またはカスタムを定義してMigrationOperationオブジェクト。There are two ways to extend the API: Using the Sql() method, or by defining custom MigrationOperation objects.

理解するには、それぞれのアプローチを使用してデータベース ユーザーを作成する操作の実装を見てみましょう。To illustrate, let's look at implementing an operation that creates a database user using each approach. この移行で、次のコードを記述できるようにします。In our migrations, we want to enable writing the following code:

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

MigrationBuilder.Sql() を使用します。Using MigrationBuilder.Sql()

呼び出す拡張メソッドを定義するカスタム操作を実装する最も簡単な方法は、MigrationBuilder.Sql()です。The easiest way to implement a custom operation is to define an extension method that calls MigrationBuilder.Sql(). 適切な Transact SQL を生成する例を次に示します。Here is an example that generates the appropriate Transact-SQL.

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

複数のデータベース プロバイダーをサポートするために、移行する場合を使えば、MigrationBuilder.ActiveProviderプロパティです。If your migrations need to support multiple database providers, you can use the MigrationBuilder.ActiveProvider property. Microsoft SQL Server と PostreSQL の両方をサポートする例を次に示します。Here's an example supporting both Microsoft SQL Server and PostreSQL.

static MigrationBuilder CreateUser(
    this MigrationBuilder migrationBuilder,
    string name,
    string password)
{
    switch (migrationBuilder.ActiveProvider)
    {
        case "Npgsql.EntityFrameworkCore.PostgreSQL":
            return migrationBuilder
                .Sql($"CREATE USER {name} WITH PASSWORD '{password}';");

        case "Microsoft.EntityFrameworkCore.SqlServer":
            return migrationBuilder
                .Sql($"CREATE USER {name} WITH PASSWORD = '{password}';");
    }
}

このアプローチだけはすべてのプロバイダーがわかっている場合、カスタムの操作が適用されます。This approach only works if you know every provider where your custom operation will be applied.

使用して、MigrationOperationUsing a MigrationOperation

SQL からカスタムの操作を切り離すことを定義できます。 自分MigrationOperationそれを表します。To decouple the custom operation from the SQL, you can define your own MigrationOperation to represent it. 操作は、ことを確認し、適切な SQL を生成するために、プロバイダーに渡されます。The operation is then passed to the provider so it can determine the appropriate SQL to generate.

class CreateUserOperation : MigrationOperation
{
    public string Name { get; set; }
    public string Password { get; set; }
}

この方法は、だけ、拡張メソッドはこれらの操作を 1 つ追加する必要があります。MigrationBuilder.Operationsです。With this approach, the extension method just needs to add one of these operations to MigrationBuilder.Operations.

static MigrationBuilder CreateUser(
    this MigrationBuilder migrationBuilder,
    string name,
    string password)
{
    migrationBuilder.Operations.Add(
        new CreateUserOperation
        {
            Name = name,
            Password = password
        });

    return migrationBuilder;
}

この方法には、SQL では、この操作を生成する方法を理解するには、各プロバイダーが必要があります、IMigrationsSqlGeneratorサービス。This approach requires each provider to know how to generate SQL for this operation in their IMigrationsSqlGenerator service. 新しい操作を処理する SQL Server のジェネレーターをオーバーライドする例を次に示します。Here is an example overriding the SQL Server's generator to handle the new operation.

class MyMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator
{
    public MyMigrationsSqlGenerator(
        MigrationsSqlGeneratorDependencies dependencies,
        IMigrationsAnnotationProvider migrationsAnnotations)
        : base(dependencies, migrationsAnnotations)
    {
    }

    protected override void Generate(
        MigrationOperation operation,
        IModel model,
        MigrationCommandListBuilder builder)
    {
        if (operation is CreateUserOperation createUserOperation)
        {
            Generate(createUserOperation, builder);
        }
        else
        {
            base.Generate(operation, model, builder);
        }
    }

    private void Generate(
        CreateUserOperation operation,
        MigrationCommandListBuilder builder)
    {
        var sqlHelper = Dependencies.SqlGenerationHelper;
        var stringMapping = Dependencies.TypeMapper.GetMapping(typeof(string));

        builder
            .Append("CREATE USER ")
            .Append(sqlHelper.DelimitIdentifier(name))
            .Append(" WITH PASSWORD = ")
            .Append(stringMapping.GenerateSqlLiteral(password))
            .AppendLine(sqlHelper.StatementTerminator)
            .EndCommand();
    }
}

1 つずつ更新で、既定の移行 sql ジェネレーターのサービスを置き換えます。Replace the default migrations sql generator service with the updated one.

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