Opérations de Migrations personnaliséCustom Migrations Operations

L’API MigrationBuilder vous permet d’effectuer différents types d’opérations lors d’une migration, mais il est loin d’être exhaustive.The MigrationBuilder API allows you to perform many different kinds of operations during a migration, but it's far from exhaustive. Toutefois, l’API est également extensible, ce qui vous permet de définir vos propres opérations.However, the API is also extensible allowing you to define your own operations. Il existe deux façons d’étendre l’API : à l’aide de la Sql() (méthode), ou en définissant personnalisé MigrationOperation objets.There are two ways to extend the API: Using the Sql() method, or by defining custom MigrationOperation objects.

Pour illustrer cela, nous allons étudier implémente une opération qui crée un utilisateur de base de données à l’aide de chaque approche.To illustrate, let's look at implementing an operation that creates a database user using each approach. Dans notre migrations, nous souhaitons pouvoir écrire le code suivant :In our migrations, we want to enable writing the following code:

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

À l’aide de MigrationBuilder.Sql()Using MigrationBuilder.Sql()

Le moyen le plus simple pour implémenter une opération personnalisée consiste à définir une méthode d’extension qui appelle MigrationBuilder.Sql().The easiest way to implement a custom operation is to define an extension method that calls MigrationBuilder.Sql(). Voici un exemple qui génère le code Transact-SQL approprié.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}';");

Si vos migrations avez besoin prendre en charge plusieurs fournisseurs de base de données, vous pouvez utiliser le MigrationBuilder.ActiveProvider propriété.If your migrations need to support multiple database providers, you can use the MigrationBuilder.ActiveProvider property. Voici un exemple de prise en charge de Microsoft SQL Server et 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;
}

Cette approche fonctionne uniquement si vous savez que chaque fournisseur où votre opération personnalisée est appliquée.This approach only works if you know every provider where your custom operation will be applied.

À l’aide d’une MigrationOperationUsing a MigrationOperation

Pour découpler l’opération personnalisée à partir de SQL, vous pouvez définir vos propres MigrationOperation pour le représenter.To decouple the custom operation from the SQL, you can define your own MigrationOperation to represent it. L’opération est ensuite transmise au fournisseur pour déterminer la requête SQL appropriée à générer.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; }
}

Avec cette approche, la méthode d’extension doit simplement ajouter une de ces opérations à 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;
}

Cette approche, chaque fournisseur doit connaître la procédure de génération SQL pour cette opération dans leurs IMigrationsSqlGenerator service.This approach requires each provider to know how to generate SQL for this operation in their IMigrationsSqlGenerator service. Voici un exemple de substitution de générateur de SQL Server pour gérer la nouvelle opération.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();
    }
}

Remplacer le service de générateur par défaut migrations sql par la mise à jour.Replace the default migrations sql generator service with the updated one.

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