EF Core 和运行时定义的架构
EF Core and a runtime-defined schema
免责声明
我试图寻找答案,但我找到的解决方案对我不起作用。
情况
我有一个 EF Core 2.2 项目,其架构在配置文件中定义。
例如,每个用户应该有自己的环境,数据tables,迁移table等。所以有一个配置文件(不一定在版本控制下),它定义了架构名称。
什么失败了
创建迁移时,EFCore 工具将架构名称(无论 运行 时是什么)硬连接到快照和迁移代码。
我设法在任意模式中创建迁移 table。
services
.AddDbContext<AppDbContext>(opt =>
opt.UseSqlServer(
cred.Database.ConnectionString,
opt => opt.MigrationsHistoryTable(
"__EFMigrationsHistory", cred.Database.Schema)));
但是数据 table 仍然在 运行 添加迁移工具时定义的架构中创建。
尝试了什么
modelBuilder.HasDefaultSchema(mSchema.Schema)
好像没有效果。
modelBuilder.Entity<>.ToTable(nameof(...), mSchema.Schema)
这(与前面的代码一起)使查询在新架构中查找 table。
我不需要玩 IModelCacheKey
,因为一次只有一个模型,所以我没有。
Remove schema parameters from migration CS file
我认为这会强制 EF Core 使用上面指定的默认架构。相反,数据 table 是在 dbo
架构中创建的。
更新
看起来,EFCore 的核心使用多个模式没有问题。它是使用硬编码模式生成迁移的添加迁移工具。无论出于何种原因,迁移和模型定义都是完全分开的。
有人能给我指出正确的方向吗?
万一有人想知道 - 我找到了解决方案。它涉及将一些代码注入 EF 迁移管道,如下所示。
在Startup.ConfigureServices
中:
services
.AddEntityFrameworkSqlServer()
.AddScoped<IMigrationsSqlGenerator, SchemaMigrationsSqlGenerator>()
.AddScoped<MigrationsSqlGenerator, SqlServerMigrationsSqlGenerator>()
.AddDbContext<AppDbContext>((serviceProvider, sqlOpt) =>
{
sqlOpt.UseInternalServiceProvider(serviceProvider)
.UseSqlServer(
connectionString,
// Make sure thge migration table is also in that schema
opt => opt.MigrationsHistoryTable("__EFMigrationsHistory", yourSchema));
});
并创建一个新的 class:
/// <summary>
/// A class injected into the SQL command generation
/// in order to replace the schema with the one we want.
/// </summary>
public sealed class SchemaMigrationsSqlGenerator : IMigrationsSqlGenerator
{
#region Fields
private readonly MigrationsSqlGenerator mOriginal;
private readonly ISchemaStorage mSchema;
#endregion
#region Init aned clean-up
/// <summary>
/// Constructor for dependency injection.
/// </summary>
/// <param name="original">Previously used SQL generator</param>
/// <param name="schema">Where the schema name is stored</param>
public SchemaMigrationsSqlGenerator(MigrationsSqlGenerator original, ISchemaStorage schema)
{
mOriginal = original;
mSchema = schema;
}
#endregion
#region IMigrationsSqlGenerator API
/// <inheritdoc />
/// <remarks>
/// Overwrite the schema generated during Add-Migration,
/// then call the original SQL generator.
/// </remarks>
IReadOnlyList<MigrationCommand> IMigrationsSqlGenerator.Generate(
IReadOnlyList<MigrationOperation> operations, IModel model)
{
foreach (var operation in operations)
{
switch (operation)
{
case SqlServerCreateDatabaseOperation _:
break;
case EnsureSchemaOperation ensureOperation:
ensureOperation.Name = mSchema.Schema;
break;
case CreateTableOperation tblOperation:
tblOperation.Schema = mSchema.Schema;
break;
case CreateIndexOperation idxOperation:
idxOperation.Schema = mSchema.Schema;
break;
default:
throw new NotImplementedException(
$"Migration operation of type {operation.GetType().Name} is not supported by SchemaMigrationsSqlGenerator.");
}
}
return mOriginal.Generate(operations, model);
}
#endregion
}
上面的代码强制迁移在注入了普通 ISchemaStorage
接口的模式中执行。
免责声明
我试图寻找答案,但我找到的解决方案对我不起作用。
情况
我有一个 EF Core 2.2 项目,其架构在配置文件中定义。
例如,每个用户应该有自己的环境,数据tables,迁移table等。所以有一个配置文件(不一定在版本控制下),它定义了架构名称。
什么失败了
创建迁移时,EFCore 工具将架构名称(无论 运行 时是什么)硬连接到快照和迁移代码。
我设法在任意模式中创建迁移 table。
services
.AddDbContext<AppDbContext>(opt =>
opt.UseSqlServer(
cred.Database.ConnectionString,
opt => opt.MigrationsHistoryTable(
"__EFMigrationsHistory", cred.Database.Schema)));
但是数据 table 仍然在 运行 添加迁移工具时定义的架构中创建。
尝试了什么
modelBuilder.HasDefaultSchema(mSchema.Schema)
好像没有效果。
modelBuilder.Entity<>.ToTable(nameof(...), mSchema.Schema)
这(与前面的代码一起)使查询在新架构中查找 table。
我不需要玩 IModelCacheKey
,因为一次只有一个模型,所以我没有。
Remove schema parameters from migration CS file
我认为这会强制 EF Core 使用上面指定的默认架构。相反,数据 table 是在 dbo
架构中创建的。
更新
看起来,EFCore 的核心使用多个模式没有问题。它是使用硬编码模式生成迁移的添加迁移工具。无论出于何种原因,迁移和模型定义都是完全分开的。
有人能给我指出正确的方向吗?
万一有人想知道 - 我找到了解决方案。它涉及将一些代码注入 EF 迁移管道,如下所示。
在Startup.ConfigureServices
中:
services
.AddEntityFrameworkSqlServer()
.AddScoped<IMigrationsSqlGenerator, SchemaMigrationsSqlGenerator>()
.AddScoped<MigrationsSqlGenerator, SqlServerMigrationsSqlGenerator>()
.AddDbContext<AppDbContext>((serviceProvider, sqlOpt) =>
{
sqlOpt.UseInternalServiceProvider(serviceProvider)
.UseSqlServer(
connectionString,
// Make sure thge migration table is also in that schema
opt => opt.MigrationsHistoryTable("__EFMigrationsHistory", yourSchema));
});
并创建一个新的 class:
/// <summary>
/// A class injected into the SQL command generation
/// in order to replace the schema with the one we want.
/// </summary>
public sealed class SchemaMigrationsSqlGenerator : IMigrationsSqlGenerator
{
#region Fields
private readonly MigrationsSqlGenerator mOriginal;
private readonly ISchemaStorage mSchema;
#endregion
#region Init aned clean-up
/// <summary>
/// Constructor for dependency injection.
/// </summary>
/// <param name="original">Previously used SQL generator</param>
/// <param name="schema">Where the schema name is stored</param>
public SchemaMigrationsSqlGenerator(MigrationsSqlGenerator original, ISchemaStorage schema)
{
mOriginal = original;
mSchema = schema;
}
#endregion
#region IMigrationsSqlGenerator API
/// <inheritdoc />
/// <remarks>
/// Overwrite the schema generated during Add-Migration,
/// then call the original SQL generator.
/// </remarks>
IReadOnlyList<MigrationCommand> IMigrationsSqlGenerator.Generate(
IReadOnlyList<MigrationOperation> operations, IModel model)
{
foreach (var operation in operations)
{
switch (operation)
{
case SqlServerCreateDatabaseOperation _:
break;
case EnsureSchemaOperation ensureOperation:
ensureOperation.Name = mSchema.Schema;
break;
case CreateTableOperation tblOperation:
tblOperation.Schema = mSchema.Schema;
break;
case CreateIndexOperation idxOperation:
idxOperation.Schema = mSchema.Schema;
break;
default:
throw new NotImplementedException(
$"Migration operation of type {operation.GetType().Name} is not supported by SchemaMigrationsSqlGenerator.");
}
}
return mOriginal.Generate(operations, model);
}
#endregion
}
上面的代码强制迁移在注入了普通 ISchemaStorage
接口的模式中执行。