Operacje migracji niestandardoweCustom Migrations Operations

Interfejs API MigrationBuilder umożliwia wykonywać wiele różnych operacji podczas migracji, ale go nie jest wyczerpująca.The MigrationBuilder API allows you to perform many different kinds of operations during a migration, but it's far from exhaustive. Interfejs API jest jednak również rozszerzalny, co pozwala zdefiniować własne operacje.However, the API is also extensible allowing you to define your own operations. Istnieją dwa sposoby, aby rozszerzyć interfejs 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ę wykonania operacji, która tworzy użytkownika bazy danych za pomocą danej metody.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");

Za pomocą MigrationBuilder.Sql()Using MigrationBuilder.Sql()

Najłatwiejszym sposobem realizowania operacji niestandardowej jest zdefiniowanie metodę rozszerzającą wywołująca MigrationBuilder.Sql().The easiest way to implement a custom operation is to define an extension method that calls MigrationBuilder.Sql(). Oto przykład, który generuje odpowiednie 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 potrzebne do obsługi wielu dostawców bazy danych, można 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 bazy danych PostgreSQL.Here's an example supporting both Microsoft SQL Server and PostgreSQL.

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}';");
    }

    return migrationBuilder;
}

To podejście tylko wtedy, gdy wiesz, co dostawcy której zostaną zastosowane niestandardowych operacji.This approach only works if you know every provider where your custom operation will be applied.

Za pomocą MigrationOperationUsing a MigrationOperation

Aby oddzielić niestandardowych operacji z bazy danych SQL, można zdefiniować własne MigrationOperation go reprezentuje.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 w celu generowania.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; }
}

W przypadku tej metody, metoda rozszerzenia musi jedynie jedną z tych operacji, aby dodać 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;
}

Takie podejście wymaga każdego dostawcy wiedzieć, jak wygenerować kodu SQL do wykonania 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 programu 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.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();
    }
}

Zaktualizowano zastąpić domyślnej migracji sql generator 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>();