Ef Core花里胡哨系列(2) 移除外键、扩展操作
Ef Core花里胡哨系列(2) 移除外键、扩展操作
虽然数据库的外键有着举足轻重的作用,但是在通常的软件设计中,很多人嫌弃麻烦,从而放弃了Ef Core
的Codo-First
模式而转向Db-First
模式。但是Db-First
不是很严谨,所以我这里就是提供一种相对折中的方式:在Code-Frist
的模式下忽略外键的生成。
总之,外键在数据库中起着重要的作用,可以确保数据的完整性和一致性,简化数据查询和操作,并帮助建立数据库的关系模型。其实还是很有必要的。
重写Ef Core
的迁移应用程序
我们无法对Ef Core
生成迁移的部分进行操作,或者说操作非常困难,但是我们可以通过重写Ef Core
中的MigrationsSqlGenerator
来实现迁移文件在向Sql
转义时的操作。
public class CustomMigrationsSqlGenerator : MigrationsSqlGenerator
{
public static ISqlManager SqlManager =>
SqlManagerHelper.GetSqlManager(AppSettings.Get<DbOptions>()?.GetUseableWriteHost()?.DbType);
public CustomMigrationsSqlGenerator(MigrationsSqlGeneratorDependencies dependencies, IMigrationsAnnotationProvider migrationsAnnotations) : base(dependencies)
{
}
protected override void Generate(Microsoft.EntityFrameworkCore.Migrations.Operations.CreateTableOperation operation, IModel? model, MigrationCommandListBuilder builder, bool terminate = true)
{
operation.ForeignKeys.Clear();
base.Generate(operation, model, builder, terminate);
}
protected override void Generate(AddForeignKeyOperation operation, IModel? model, MigrationCommandListBuilder builder,
bool terminate = true)
{
return;
}
}
替换Ef Core
中的默认实现
services.AddDbContext<SampleDbContext>(opts =>
{
opts.ReplaceService<IMigrationsSqlGenerator, CustomMigrationsSqlGenerator>();
});
扩展操作
SqlManager
是我自己封装的一个Sql生成相关的类,与逻辑无关,可以自己根据需要自行封装。
1 修改表
Ef Core
中微软的官方实现为在修改列时,操作为先删除列再添加,我们可以通过重写对应的方法来实现直接修改的操作。
具体就是可以自己根据operation
生成Sql
使用builder.Append(sql).AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator).EndCommand();
来执行。
一些社区的
provider
是实现了对应的修改方法的,但是微软官方库没有,可以使用这种方法补充。
protected override void Generate(AlterColumnOperation operation, IModel? model, MigrationCommandListBuilder builder)
{
var sql = SqlManager.Block(SqlManager.GetAlterColumnSql(control, operation.Table,
oldControl, true)).TrimEnd(';', '\n', '\r');
builder.Append(sql).AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator).EndCommand();
}
2 备份表
在进行比较具有风险的迁移操作时,可以采用上面类似的方式,将表迁移前的数据和结构导入到一个历史表中。
GetBackupTableSql
即根据当前表名将数据导入一个按操作时间命名的表名,例如User20240102
。
protected override void Generate(DropTableOperation operation, IModel? model, MigrationCommandListBuilder builder, bool terminate = true)
{
// 删除表和列时会对整个表进行备份,防止使用Migrations的时候误删除
var sql = SqlManager.Block(SqlManager.GetBackupTableSql(operation.Name)).TrimEnd(';', '\r', '\n');
builder.Append(sql).AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator).EndCommand();
base.Generate(operation, model, builder, terminate);
}
protected override void Generate(DropColumnOperation operation, IModel? model, MigrationCommandListBuilder builder, bool terminate = true)
{
// 删除表和列时会对整个表进行备份,防止使用Migrations的时候误删除
var sql = SqlManager.Block(SqlManager.GetBackupTableSql(operation.Table)).TrimEnd(';', '\r', '\n');
builder.Append(sql).AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator).EndCommand();
base.Generate(operation, model, builder, terminate);
}