Benutzerdefinierte MigrationsoperationenCustom Migrations Operations

Die MigrationBuilder-API können Sie viele verschiedene Arten von Vorgängen bei der Migration ausführen, aber er ist längst nicht vollständig.The MigrationBuilder API allows you to perform many different kinds of operations during a migration, but it's far from exhaustive. Die API ist jedoch auch erweiterbar und ermöglichen Ihnen, Ihre eigenen Vorgänge zu definieren.However, the API is also extensible allowing you to define your own operations. Es gibt zwei Möglichkeiten, um die API zu erweitern: Verwenden der Sql() -Methode, oder durch Definieren von benutzerdefinierten MigrationOperation Objekte.There are two ways to extend the API: Using the Sql() method, or by defining custom MigrationOperation objects.

Um zu veranschaulichen, betrachten wir einen Vorgang, der erstellt einen Datenbankbenutzer, der mit jeder Ansatz, zu implementieren.To illustrate, let's look at implementing an operation that creates a database user using each approach. In unserem Migrationen möchten wir aktivieren Sie den folgenden Code zu schreiben:In our migrations, we want to enable writing the following code:

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

Verwenden von MigrationBuilder.Sql()Using MigrationBuilder.Sql()

Die einfachste Möglichkeit zum Implementieren eines benutzerdefinierten Vorgangs ist eine Erweiterungsmethode zu definieren, die aufruft MigrationBuilder.Sql().The easiest way to implement a custom operation is to define an extension method that calls MigrationBuilder.Sql(). Hier ist ein Beispiel, das die entsprechenden Transact-SQL generiert.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}';");

Wenn Sie Ihre Migrationen mehrere Datenbankanbieter unterstützen müssen, können Sie mithilfe der MigrationBuilder.ActiveProvider Eigenschaft.If your migrations need to support multiple database providers, you can use the MigrationBuilder.ActiveProvider property. Hier ist ein Beispiel für Unterstützung von Microsoft SQL Server und 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;
}

Dieser Ansatz funktioniert nur, wenn Sie wissen, dass jeder Anbieter, wo der benutzerdefinierte Vorgang angewendet werden.This approach only works if you know every provider where your custom operation will be applied.

Verwenden eine MigrationOperationUsing a MigrationOperation

Um der benutzerdefinierte Vorgang aus dem SQL zu entkoppeln, Sie können Ihren eigenen definieren MigrationOperation für die Darstellung.To decouple the custom operation from the SQL, you can define your own MigrationOperation to represent it. Der Vorgang wird dann an den Anbieter übergeben, damit sie die entsprechende SQL generieren bestimmen kann.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; }
}

Bei diesem Ansatz die Erweiterungsmethode muss lediglich eine von diesen Vorgängen hinzufügen 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;
}

Dieser Ansatz erfordert jeder Anbieter wissen, wie Sie für diesen Vorgang im SQL generieren die IMigrationsSqlGenerator Service.This approach requires each provider to know how to generate SQL for this operation in their IMigrationsSqlGenerator service. Hier ist ein Beispiel, das Überschreiben der SQL Server Generator aus, um den neuen Vorgang zu verarbeiten.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();
    }
}

Ersetzen Sie den Standarddienst Migrationen Sql-Generator, mit dem aktualisierten.Replace the default migrations sql generator service with the updated one.

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