Özel geçiş IşlemleriCustom Migrations Operations

MigrationBuilder API 'SI, geçiş sırasında birçok farklı türde işlem gerçekleştirmenize olanak tanır, ancak bu çok yoğun bir şekilde yapılır.The MigrationBuilder API allows you to perform many different kinds of operations during a migration, but it's far from exhaustive. Ancak, API ayrıca kendi işlemlerinizi tanımlamanızı sağlayan genişletilebilir.However, the API is also extensible allowing you to define your own operations. API 'yi genişletmek için iki yol vardır: Sql() yöntemini kullanma veya özel MigrationOperation nesneleri tanımlama.There are two ways to extend the API: Using the Sql() method, or by defining custom MigrationOperation objects.

Göstermek için, her yaklaşımı kullanarak bir veritabanı kullanıcısı oluşturan bir işlem uygulamaya bakalım.To illustrate, let's look at implementing an operation that creates a database user using each approach. Geçişlerimiz bölümünde aşağıdaki kodun yazılmasını etkinleştirmek istiyoruz:In our migrations, we want to enable writing the following code:

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

MigrationBuilder. SQL () kullanmaUsing MigrationBuilder.Sql()

Özel bir işlem uygulamanın en kolay yolu, MigrationBuilder.Sql()çağıran bir genişletme yöntemi tanımlamaktır.The easiest way to implement a custom operation is to define an extension method that calls MigrationBuilder.Sql(). Uygun Transact-SQL üreten bir örnek aşağıda verilmiştir.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}';");

Geçişlerinizin birden çok veritabanı sağlayıcısını desteklemesi gerekiyorsa MigrationBuilder.ActiveProvider özelliğini kullanabilirsiniz.If your migrations need to support multiple database providers, you can use the MigrationBuilder.ActiveProvider property. Hem Microsoft SQL Server hem de PostgreSQL destekleyen bir örnek aşağıda verilmiştir.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;
}

Bu yaklaşım yalnızca özel işlemin uygulanacağı her sağlayıcıyı biliyorsanız çalışır.This approach only works if you know every provider where your custom operation will be applied.

MigrationOperation kullanmaUsing a MigrationOperation

Özel işlemi SQL 'den ayırmak için, bunu temsil etmek üzere kendi MigrationOperation tanımlayabilirsiniz.To decouple the custom operation from the SQL, you can define your own MigrationOperation to represent it. Daha sonra işlem sağlayıcıya geçirilir, böylece oluşturulacak uygun SQL 'i tespit edebilir.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; }
}

Bu yaklaşımla, Uzantı yönteminin MigrationBuilder.Operationsiçin bu işlemlerden birini eklemesi yeterlidir.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;
}

Bu yaklaşım, her sağlayıcının IMigrationsSqlGenerator hizmetinde bu işlem için nasıl SQL üreteceğimizi bilmesini gerektirir.This approach requires each provider to know how to generate SQL for this operation in their IMigrationsSqlGenerator service. İşte, yeni işlemi işlemek için SQL Server üreticisini geçersiz kılan bir örnek.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();
    }
}

Varsayılan geçişleri SQL Oluşturucu hizmetini güncelleştirilmiş bir ile değiştirin.Replace the default migrations sql generator service with the updated one.

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