【发布时间】:2021-08-17 03:52:33
【问题描述】:
我想关闭 Entity Framework Core 中的 ALL(或至少大部分)约定(我说的是 EF Core 5 或更高版本),然后“手动”构建整个模型.
可能有人想知道为什么。
原因如下:我的任务是将几个大型遗留数据库从 Entity Framework 6 (EF) 迁移到 Entity Framework Core 5 (EFC)。这涉及数百个表和几个数据库。其中一些数据库是使用 Code First 方法创建的,而有些只是第三方数据库,我们需要从 C# 代码中查询和更新这些数据库。对于后面的数据库,我们必须完全匹配它们的模式。
由于问题的规模,EF 和EFC 两种代码风格必须共存,比如说几个月。这可以通过使用条件编译轻松实现(见下文)。
与EF 相比,EFC 很可能不支持或不方便支持的任何内容(或被“入侵”到EF 模型中),例如空间索引、多列 KeyAttribute PK、多-column ForeignKeyAttribute FKs,自引用多个乘法表,在同一列上定义的多个索引(有些是过滤器,有些只是常规索引),等等等等。
没关系。我可以通过使用条件编译“覆盖”属性来轻松处理EFC inability,例如
#if EFCORE
using Key = MyKeyAttribute;
using Column = MyColumnAttribute;
using Index = MyIndexAttribute;
using ForeignKey = MyForeignKeyAttribute;
#endif
然后为每个MyProject.csproj 创建一个MyProject_EFC.csproj,其中定义了EFCORE,然后使用反射“收集”所有这些自定义属性,然后使用EFC Fluent API 配置EFC 的所有内容做不到。因此,遗留 (EF) 代码仍将看到原始代码,例如KeyAttribute 然后遵循EF 路线,而EFC 代码将看不到属性,因为它们被重新定义。所以,它不会抱怨。 我已经有了所有这些代码,它可以工作,也许我会在某个时候把它放在这里或放在 GitHub 中,但不是今天。
让我抓狂的是,无论我做什么,EFC 都会设法“潜入”阴影属性和类似的蹩脚的东西。这就到了我真的想关闭 ALL EFC 约定并手动构建整个模型的地步。毕竟,我已经在这样做了,就像 90% 的模型一样。我宁愿EFC throw(带有有意义的错误消息)也不愿默默地做任何我不希望它做的事情。
遵循@IvanStoev 的建议是我目前所拥有的:
public static IModel CreateModel<TContext, TContextInfo>(Action<ModelBuilder, TContextInfo>? modifier = null)
where TContext : DbContext, ISwyfftDbContext
where TContextInfo : ContextInfo<TContext>, new()
{
var contextInfo = new TContextInfo();
var modelBuilder = new ModelBuilder();
modelBuilder
.HasKeys<TContext, TContextInfo>(contextInfo)
.HasColumnNames<TContext, TContextInfo>(contextInfo)
.ToTables<TContext, TContextInfo>(contextInfo)
.DisableCascadeDeletes()
.HasDefaultValues<TContext, TContextInfo>(contextInfo)
.HasComputedColumns<TContext, TContextInfo>(contextInfo)
.HasForeignKeys<TContext, TContextInfo>(contextInfo)
.HasDatabaseIndexes<TContext, TContextInfo>(contextInfo);
modifier?.Invoke(modelBuilder, contextInfo);
var model = modelBuilder.FinalizeRelationalModel();
return model;
}
private static IModel FinalizeRelationalModel(this ModelBuilder modelBuilder)
{
var model = modelBuilder.Model;
var conventionModel = model as IConventionModel;
var databaseModel = new RelationalModel(model);
conventionModel.SetAnnotation(RelationalAnnotationNames.RelationalModel, databaseModel);
return modelBuilder.FinalizeModel();
}
其中HasKeys、HasColumnNames 等是我[较早] 编写的扩展方法,用于继续使用多列 PK、Fs 等,EFC 不支持,conventionModel.SetAnnotation(RelationalAnnotationNames.RelationalModel, databaseModel) 是强制性的否则不会创建模型并且代码因 NRE 失败。
所以,当我将这个CreateModel 插入DbContextOptions 时:
public static DbContextOptions<TContext> GetDbContextOptions(string connectionString, Func<IModel> modelCreator) =>
new DbContextOptionsBuilder<TContext>()
.UseModel(modelCreator())
.UseSqlServer(connectionString, x => x.UseNetTopologySuite())
.Options;
并通过运行例如创建迁移Add-Migration Initial 然后 ModelSnapshot 最终结果是正确的,没有垃圾阴影属性,也没有其他废话 EFC 在这里或那里插入所有约定。但是,当我尝试查询任何表时,代码失败:
(InvalidOperationException) Sequence contains no elements;
Sequence contains no elements ( at System.Linq.ThrowHelper.ThrowNoElementsException()
at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
at Microsoft.EntityFrameworkCore.Query.SqlExpressions.SelectExpression..ctor(IEntityType entityType, ISqlExpressionFactory sqlExpressionFactory)
at Microsoft.EntityFrameworkCore.Query.SqlExpressionFactory.Select(IEntityType entityType)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.CreateShapedQueryExpression(IEntityType entityType)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitExtension(Expression extensionExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitExtension(Expression extensionExpression)
at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.<Execute>b__0()
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToQueryString(IQueryable source)
这意味着RelationalModel 严重不完整。
任何进一步的想法都将受到高度赞赏。非常感谢!
【问题讨论】:
标签: c# entity-framework-core ef-core-5.0