Operacje migracji niestandardowychCustom Migrations Operations

Interfejs API MigrationBuilder umożliwia wykonywać wiele różnych operacji podczas migracji, ale jest daleko od wyczerpujący.The MigrationBuilder API allows you to perform many different kinds of operations during a migration, but it's far from exhaustive. Jednak interfejsu API jest również możliwość definiowania własnych operacji extensible.However, the API is also extensible allowing you to define your own operations. Istnieją dwa sposoby rozszerzania interfejsu API: przy użyciu Sql() metody, lub przez zdefiniowanie niestandardowego MigrationOperation obiektów.There are two ways to extend the API: Using the Sql() method, or by defining custom MigrationOperation objects.

Aby zilustrować, Przyjrzyjmy się implementacja operację, która tworzy użytkownika bazy danych przy użyciu każdego z podejść.To illustrate, let's look at implementing an operation that creates a database user using each approach. W naszym migracje chcemy włączenia zapisywania następujący kod:In our migrations, we want to enable writing the following code:

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

Przy użyciu MigrationBuilder.Sql()Using MigrationBuilder.Sql()

Najprostszym sposobem, aby zaimplementować to operacja niestandardowa jest określenie metodę rozszerzenia, która wywołuje MigrationBuilder.Sql().The easiest way to implement a custom operation is to define an extension method that calls MigrationBuilder.Sql(). Oto przykład generujący odpowiedniego języka 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}';");

Jeśli migracji konieczność obsługi wielu dostawców bazy danych, możesz użyć MigrationBuilder.ActiveProvider właściwości.If your migrations need to support multiple database providers, you can use the MigrationBuilder.ActiveProvider property. Oto przykład obsługi zarówno programu Microsoft SQL Server, jak i 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}';");
    }
}

Ta metoda działa tylko, jeśli znasz każdego dostawcy których zostaną zastosowane niestandardowych operacji.This approach only works if you know every provider where your custom operation will be applied.

Przy użyciu MigrationOperationUsing a MigrationOperation

Rozdzielenie operacja niestandardowa z SQL, można zdefiniować własny MigrationOperation do reprezentowania go.To decouple the custom operation from the SQL, you can define your own MigrationOperation to represent it. Operacja są następnie przekazywane do dostawcy, dzięki czemu można określić odpowiednie SQL do wygenerowania.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; }
}

Z tej metody, metoda rozszerzenia musi dodać jeden z tych operacji 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;
}

Ta metoda wymaga każdego dostawcy dowiedzieć się, jak można wygenerować kodu SQL dla tej operacji w ich IMigrationsSqlGenerator usługi.This approach requires each provider to know how to generate SQL for this operation in their IMigrationsSqlGenerator service. Oto przykład zastępowanie generator SQL Server do obsługi nowej operacji.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();
    }
}

Zaktualizowano zastąpić domyślne migracje sql generatora usługi.Replace the default migrations sql generator service with the updated one.

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