Dela via


Åtgärder för anpassade migreringar

Med Api:et MigrationBuilder kan du utföra många olika typer av åtgärder under en migrering, men det är långt ifrån uttömmande. API:et är dock också utökningsbart så att du kan definiera dina egna åtgärder. Det finns två sätt att utöka API:et: Använda Sql() metoden eller genom att definiera anpassade MigrationOperation objekt.

För att illustrera ska vi titta på hur du implementerar en åtgärd som skapar en databasanvändare med varje metod. I våra migreringar vill vi aktivera skrivning av följande kod:

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

Använda MigrationBuilder.Sql()

Det enklaste sättet att implementera en anpassad åtgärd är att definiera en tilläggsmetod som anropar MigrationBuilder.Sql(). Här är ett exempel som genererar rätt Transact-SQL.

public static OperationBuilder<SqlOperation> CreateUser(
    this MigrationBuilder migrationBuilder,
    string name,
    string password)
    => migrationBuilder.Sql($"CREATE USER {name} WITH PASSWORD '{password}';");

Tips/Råd

På SQL Server använder du EXEC funktionen när en instruktion måste vara den första eller bara en i en SQL-batch. Det kan också behövas för att kringgå parsningsfel i idempotent-migreringsskript som kan inträffa när refererade kolumner för närvarande inte finns i en tabell.

Om dina migreringar behöver stöd för flera databasprovidrar kan du använda MigrationBuilder.ActiveProvider egenskapen . Här är ett exempel som stöder både Microsoft SQL Server och PostgreSQL.

public static OperationBuilder<SqlOperation> 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}';");
    }

    throw new Exception("Unexpected provider.");
}

Den här metoden fungerar bara om du känner till alla leverantörer där din anpassade åtgärd kommer att tillämpas.

Använda en Migrationsoperation

Om du vill frikoppla den anpassade åtgärden från SQL kan du definiera din egen MigrationOperation för att representera den. Åtgärden skickas sedan till providern så att den kan fastställa vilken SQL som ska genereras.

public class CreateUserOperation : MigrationOperation
{
    public string Name { get; set; }
    public string Password { get; set; }
}

Med den här metoden behöver tilläggsmetoden bara lägga till en av dessa åtgärder i MigrationBuilder.Operations.

public static OperationBuilder<CreateUserOperation> CreateUser(
    this MigrationBuilder migrationBuilder,
    string name,
    string password)
{
    var operation = new CreateUserOperation { Name = name, Password = password };
    migrationBuilder.Operations.Add(operation);

    return new OperationBuilder<CreateUserOperation>(operation);
}

Den här metoden kräver att varje provider vet hur man genererar SQL för den här åtgärden i sin IMigrationsSqlGenerator tjänst. Här är ett exempel som åsidosättar SQL Server-generatorn för att hantera den nya åtgärden.

public class MyMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator
{
    public MyMigrationsSqlGenerator(
        MigrationsSqlGeneratorDependencies dependencies,
        ICommandBatchPreparer commandBatchPreparer)
        : base(dependencies, commandBatchPreparer)
    {
    }

    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();
    }
}

Ersätt sql generator-tjänsten för standardmigrering med den uppdaterade.

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