如何将Ef Core外键、扩展操作简化为?

摘要:Ef Core花里胡哨系列(2) 移除外键、扩展操作 虽然数据库的外键有着举足轻重的作用,但是在通常的软件设计中,很多人嫌弃麻烦,从而放弃了Ef Core的Codo-First模式而转向Db-First模式。但是Db-First不是很严谨,
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); }