カスタムの移行操作
MigrationBuilder API を使用すると、移行中にさまざまな種類の操作を実行できますが、すべての操作を網羅しているわけではありません。 ただし、この API は拡張できるため、独自の操作を定義することもできます。 この API を拡張する場合、Sql()
メソッドを使用する、またはカスタム MigrationOperation
オブジェクトを定義するという 2 つの方法があります。
例として、データベース ユーザーを作成する操作の実装を、それぞれのアプローチで行う方法を見てみましょう。 この移行では、次のコードを記述できるようにします。
migrationBuilder.CreateUser("SQLUser1", "Password");
MigrationBuilder.Sql() を使用する
カスタム操作を実装する最も簡単な方法は、MigrationBuilder.Sql()
を呼び出す拡張メソッドを定義することです。 適切な Transact-SQL を生成する例を次に示します。
public static OperationBuilder<SqlOperation> CreateUser(
this MigrationBuilder migrationBuilder,
string name,
string password)
=> migrationBuilder.Sql($"CREATE USER {name} WITH PASSWORD '{password}';");
ヒント
ステートメントが SQL バッチ内の最初または唯一のものでなければならない場合は、EXEC
関数を使用します。 また、参照先の列が現在テーブルに存在しない場合に発生する可能性がある、べき等移行スクリプトのパーサー エラーに対処することも必要になる場合があります。
移行で複数のデータベース プロバイダーをサポートする必要がある場合は、MigrationBuilder.ActiveProvider
プロパティを使用できます。 Microsoft SQL Server と 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.");
}
このアプローチは、カスタム操作が適用されるすべてのプロバイダーがわかっている場合にのみ機能します。
MigrationOperation を使用する
カスタム操作を SQL から切り離す場合は、それを表す独自の MigrationOperation
を定義できます。 その後、この操作はプロバイダーに渡され、生成する適切な SQL を決定できます。
public class CreateUserOperation : MigrationOperation
{
public string Name { get; set; }
public string Password { get; set; }
}
このアプローチを使用する場合、拡張メソッドによってこれらの操作のいずれかを 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);
}
このアプローチでは、各プロバイダーが IMigrationsSqlGenerator
サービスでこの操作の SQL を生成する方法を知っている必要があります。 新しい操作を処理するために SQL Server をオーバーライドする例を次に示します。
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();
}
}
既定の移行 SQL ジェネレーター サービスを更新したものに置き換えます。
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options
.UseSqlServer(_connectionString)
.ReplaceService<IMigrationsSqlGenerator, MyMigrationsSqlGenerator>();
.NET