Operacje migracji niestandardowych

Interfejs API MigrationBuilder umożliwia wykonywanie wielu różnych rodzajów operacji podczas migracji, ale nie jest wyczerpujące. Jednak interfejs API jest również rozszerzalny, co umożliwia definiowanie własnych operacji. Istnieją dwa sposoby rozszerzania interfejsu API: używanie Sql() metody lub definiowanie obiektów niestandardowych MigrationOperation .

Aby zilustrować, przyjrzyjmy się implementacji operacji, która tworzy użytkownika bazy danych przy użyciu każdego podejścia. W naszych migracjach chcemy włączyć pisanie następującego kodu:

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

Korzystanie z elementu MigrationBuilder.Sql()

Najprostszym sposobem zaimplementowania operacji niestandardowej jest zdefiniowanie metody rozszerzenia, która wywołuje MigrationBuilder.Sql()metodę . Oto przykład, który generuje odpowiedni język Transact-SQL.

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

Napiwek

EXEC Użyj funkcji , gdy instrukcja musi być pierwszą lub tylko jedną w partii SQL. Może być również konieczne obejście błędów analizatora w skryptach migracji idempotentnych, które mogą wystąpić, gdy przywoływanych kolumn nie istnieją obecnie w tabeli.

Jeśli migracje muszą obsługiwać wielu dostawców baz danych, możesz użyć MigrationBuilder.ActiveProvider właściwości . Oto przykład obsługujący zarówno programy Microsoft SQL Server, jak i 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.");
}

Takie podejście działa tylko wtedy, gdy znasz każdego dostawcę, w którym zostanie zastosowana operacja niestandardowa.

Korzystanie z elementu MigrationOperation

Aby rozdzielić operację niestandardową z bazy danych SQL, możesz zdefiniować własną MigrationOperation , aby ją reprezentować. Operacja jest następnie przekazywana do dostawcy, aby mogła określić odpowiedni kod SQL do wygenerowania.

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

W przypadku tego podejścia metoda rozszerzenia musi tylko dodać jedną z tych operacji do metody 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);
}

Takie podejście wymaga, aby każdy dostawca wiedział, jak wygenerować język SQL dla tej operacji w swojej IMigrationsSqlGenerator usłudze. Oto przykład zastąpienia generatora programu SQL Server w celu obsługi nowej operacji.

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();
    }
}

Zastąp domyślną usługę generatora sql zaktualizowaną usługą .

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