次の方法で共有


カスタム移行操作

MigrationBuilder API を使用すると、移行中にさまざまな種類の操作を実行できますが、網羅的ではありません。 ただし、API は拡張可能であり、独自の操作を定義できます。 API を拡張するには、Sql() メソッドを使用するか、カスタム MigrationOperation オブジェクトを定義する方法の 2 つがあります。

説明するために、各アプローチを使用してデータベース ユーザーを作成する操作の実装を見てみましょう。 移行では、次のコードの記述を有効にします。

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

MigrationBuilder.Sql() の使用

カスタム操作を実装する最も簡単な方法は、MigrationBuilder.Sql()を呼び出す拡張メソッドを定義することです。 適切な Transact-SQL を生成する例を次に示します。

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

ヒント

SQL Server では、ステートメントが SQL バッチの最初のステートメントまたは唯一のステートメントである必要がある場合は、EXEC 関数を使用します。 参照される列がテーブルに現在存在しない場合に発生する可能性がある冪等移行スクリプトのパーサーエラーを回避する必要になることもあります。

移行で複数のデータベース プロバイダーをサポートする必要がある場合は、MigrationBuilder.ActiveProvider プロパティを使用できます。 Microsoft SQL Server と PostgreSQL の両方をサポートする例を次に示します。

public static OperationBuilder<SqlOperation> 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}';");
    }

    throw new Exception("Unexpected provider.");
}

この方法は、カスタム操作が適用されるすべてのプロバイダーがわかっている場合にのみ機能します。

MigrationOperation を使用する

カスタム操作を SQL から切り離すには、それを表す独自の MigrationOperation を定義します。 その後、生成する適切な SQL を決定できるように、操作がプロバイダーに渡されます。

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

この方法では、拡張メソッドは、これらの操作のいずれかを MigrationBuilder.Operationsに追加するだけで済みます。

public static OperationBuilder<CreateUserOperation> CreateUser(
    this MigrationBuilder migrationBuilder,
    string name,
    string password)
{
    var operation = new CreateUserOperation { Name = name, Password = password };
    migrationBuilder.Operations.Add(operation);

    return new OperationBuilder<CreateUserOperation>(operation);
}

この方法では、各プロバイダーは、IMigrationsSqlGenerator サービスでこの操作の SQL を生成する方法を知っている必要があります。 新しい操作を処理するために SQL Server のジェネレーターをオーバーライドする例を次に示します。

public class MyMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator
{
    public MyMigrationsSqlGenerator(
        MigrationsSqlGeneratorDependencies dependencies,
        ICommandBatchPreparer commandBatchPreparer)
        : base(dependencies, commandBatchPreparer)
    {
    }

    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.TypeMappingSource.FindMapping(typeof(string));

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

既定の移行 SQL ジェネレーター サービスを更新された移行サービスに置き換えます。

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