Operace vlastních migrací
Rozhraní API MigrationBuilder umožňuje provádět během migrace mnoho různých druhů operací, ale zdaleka není vyčerpávající. Rozhraní API je ale také rozšiřitelné a umožňuje definovat vlastní operace. Rozhraní API můžete rozšířit dvěma způsoby: pomocí Sql()
metody nebo definováním vlastních MigrationOperation
objektů.
Pojďme se podívat na implementaci operace, která vytvoří uživatele databáze pomocí jednotlivých přístupů. V našich migracích chceme povolit psaní následujícího kódu:
migrationBuilder.CreateUser("SQLUser1", "Password");
Použití MigrationBuilder.Sql()
Nejjednodušší způsob, jak implementovat vlastní operaci, je definovat rozšiřující metodu, která volá MigrationBuilder.Sql()
. Tady je příklad, který vygeneruje odpovídající transact-SQL.
public static OperationBuilder<SqlOperation> CreateUser(
this MigrationBuilder migrationBuilder,
string name,
string password)
=> migrationBuilder.Sql($"CREATE USER {name} WITH PASSWORD '{password}';");
Tip
EXEC
Funkci použijte, když příkaz musí být první nebo pouze jeden v dávce SQL. Může být také potřeba vyřešit chyby analyzátoru ve skriptech idempotentní migrace, ke kterým může dojít, když odkazované sloupce v tabulce aktuálně neexistují.
Pokud vaše migrace potřebuje podporovat více zprostředkovatelů databáze, můžete tuto vlastnost použít MigrationBuilder.ActiveProvider
. Tady je příklad podpory Microsoft SQL Serveru i 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.");
}
Tento přístup funguje jenom v případě, že znáte každého poskytovatele, u kterého se použije vaše vlastní operace.
Použití migrationOperation
Pokud chcete oddělit vlastní operaci od SQL, můžete definovat vlastní MigrationOperation
, která ji bude reprezentovat. Operace se pak předá poskytovateli, aby mohl určit odpovídající SQL, který se má vygenerovat.
public class CreateUserOperation : MigrationOperation
{
public string Name { get; set; }
public string Password { get; set; }
}
S tímto přístupem stačí, aby metoda rozšíření přidala jednu z těchto operací do 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);
}
Tento přístup vyžaduje, aby každý poskytovatel věděl, jak vygenerovat SQL pro tuto operaci ve své službě IMigrationsSqlGenerator
. Tady je příklad přepsání generátoru SQL Serveru pro zpracování nové operace.
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();
}
}
Nahraďte výchozí službu generátoru SQL aktualizovanou službou.
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options
.UseSqlServer(_connectionString)
.ReplaceService<IMigrationsSqlGenerator, MyMigrationsSqlGenerator>();