사용자 지정 마이그레이션 작업
MigrationBuilder API를 사용하면 마이그레이션 중에 다양한 종류의 작업을 수행할 수 있지만 이것이 전부는 아닙니다. 그러나 API는 확장 가능하므로 사용자 고유의 작업을 정의할 수 있습니다. API를 확장하는 방법에는 Sql()
메서드를 사용하거나 사용자 지정 MigrationOperation
개체를 정의하는 두 가지 방법이 있습니다.
설명을 위해 각 접근 방식을 사용하여 데이터베이스 사용자를 만드는 작업을 구현해 보겠습니다. 마이그레이션에서 다음 코드 작성을 사용하려고 합니다.
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
함수를 사용합니다. 참조된 열이 현재 테이블에 없을 때 발생할 수 있는 idempotent 마이그레이션 스크립트의 파서 오류를 해결해야 할 수도 있습니다.
마이그레이션에서 여러 데이터베이스 공급자를 지원해야 하는 경우 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