配置EF Core模型时遇CS0311错误,求原因及相关解惑
问题背景
为遵循DRY原则复用代码,配置EF Core模型时编写了GetProperties方法筛选所有实体类型的属性,但触发编译错误。
问题代码
public List<IMutableProperty> GetProperties(ModelBuilder modelBuilder, Func<IProperty, bool> filter) { var properties = modelBuilder.Model .GetEntityTypes() .SelectMany(t => t.GetProperties()) .ToList() .Where(filter) // 此行触发错误 .ToList(); return properties; }
错误信息
error CS0311: The type 'Microsoft.EntityFrameworkCore.Metadata.IProperty' cannot be used as type parameter 'TRow' in the generic type or method 'TypedTableBaseExtensions.Where(TypedTableBase
, Func<TRow, bool>)'. There is no implicit reference conversion from 'Microsoft.EntityFrameworkCore.Metadata.IProperty' to 'System.Data.DataRow'.
使用示例
protected override void OnModelCreating(ModelBuilder modelBuilder) { ConfigureTenants(modelBuilder); ConfigureLocales(modelBuilder); ConfigureKeys(modelBuilder); ConfigureGuids(modelBuilder); ConfigureSlugs(modelBuilder); ConfigureOrders(modelBuilder); ConfigRelatedItems(modelBuilder); } public void ConfigureTenants(ModelBuilder modelBuilder) { var properties = GetProperties(modelBuilder, p => p.ClrType == typeof(string) && p.Name == "Tenant"); properties.ForEach(p => p.SetDefaultValue("'main'")); } public void ConfigureLocales(ModelBuilder modelBuilder) { var properties = GetProperties(modelBuilder, p => p.ClrType == typeof(string) && p.Name == "Locale"); properties.ForEach(p => p.SetDefaultValue("'en'")); }
疑问
为何会出现该错误?此代码与TRow究竟有何关联?
错误原因与修复方案
错误原因
问题出在调用.ToList()后再使用.Where():编译器错误绑定了System.Data.DataSetExtensions命名空间下的TypedTableBaseExtensions.Where方法(该方法专为DataRow类型的表设计),而非LINQ的Enumerable.Where扩展方法。
具体逻辑:
- 先将EF Core属性集合转为
List<IProperty> - 编译器找不到匹配的LINQ
Where时,误匹配了另一个命名空间的Where方法,而该方法要求参数为DataRow类型,与IProperty不兼容,因此触发类型转换错误。
修复方案
方案一:移除多余的.ToList()
让Where直接作用于EF Core返回的可枚举集合,编译器会正确绑定LINQ的Enumerable.Where方法:
public List<IMutableProperty> GetProperties(ModelBuilder modelBuilder, Func<IProperty, bool> filter) { var properties = modelBuilder.Model .GetEntityTypes() .SelectMany(t => t.GetProperties()) .Where(filter) .ToList(); return properties; }
方案二:显式指定LINQ方法(不推荐,仅作演示)
若必须先转List再过滤,可通过AsEnumerable()将集合转为IEnumerable<T>,引导编译器找到正确的Where方法:
using System.Linq; // ... var properties = modelBuilder.Model .GetEntityTypes() .SelectMany(t => t.GetProperties()) .ToList() .AsEnumerable() .Where(filter) .ToList();
额外提示
注意默认值写法:SetDefaultValue("'main'")会将带单引号的字符串作为默认值,若要设置的是字符串main而非'main',应改为SetDefaultValue("main")。
内容的提问来源于stack exchange,提问作者Hasan

