Entity Framework LINQ查询继承维度实体类型转换报错解决方案
问题描述
我有大量带有层级编码的不同数据表,编码层级从1到5,具体取决于表对应的业务领域,可以基于这些编码实现不同粒度的数据搜索。我做了一个简化示例,对应的EF实体都已添加到dbcontext中,代码如下:
public interface IDim1 { int Id { get; set; } string Dim1Code { get; set; } } public class Dim1 : IDim1 { public int Id { get; set; } public string Dim1Code { get; set; } } public interface IDim2 : IDim1 { string Dim2Code { get; set; } } public class Dim2 : IDim2 { public string Dim2Code { get; set; } } public interface IDim3 : IDim2 { string Dim3Code { get; set; } } public class Dim3 : Dim2, IDim3 { public string Dim3Code { get; set; } } public class House : Dim2 { public string House { get; set; } } public class Car : Dim1 { public string Car { get; set; } } public class Work : Dim3 { public string Employeer { get; set; } } public bool DimensionCodeExists<T>(string code) where T : Dim1 { var Queryable = dbcontext.Set<T>().AsQueryable(); if (typeof(Dim3).IsAssignableFrom(typeof(T))) { // 问题出在这里 return Queryable.Cast<Dim3>().Any(o => o.Dim3Code == code || o.Dim2Code == code || o.Dim1Code == code); } else if (typeof(Dim2).IsAssignableFrom(typeof(T))) { // 此处也存在同样问题 return Queryable.Cast<Dim2>().Any(o => o.Dim2Code == code || o.Dim1Code == code); } else { // 此处同样有问题 return Queryable.Cast<Dim1>().Any(o => o.Dim1Code == code); } }
当前遇到的问题是:我需要查询匹配对应层级的编码,但无法将泛型实体转换为Dim3/Dim2/Dim1类型,否则EF会抛出表达式错误:LINQ to Entities only supports casting Entity Data Model primitive types。
我已经确认泛型参数T具备对应层级的编码属性,请问如何实现无需强制类型转换即可完成对应编码的匹配查询?
解决方案
你可以通过动态构建表达式树生成对应层级的查询条件,完全避免类型转换操作,EF可以正常解析这类动态生成的表达式。
实现代码
using System.Linq.Expressions; public bool DimensionCodeExists<T>(string code) where T : Dim1 { // 构造Lambda参数 o => ... var parameter = Expression.Parameter(typeof(T), "o"); var conditionExpressions = new List<Expression>(); // 所有T都继承自Dim1,必然有Dim1Code属性,先加对应判断 var dim1CodeProp = Expression.Property(parameter, nameof(IDim1.Dim1Code)); conditionExpressions.Add(Expression.Equal(dim1CodeProp, Expression.Constant(code))); // 若T实现了IDim2接口,追加Dim2Code的匹配判断 if (typeof(IDim2).IsAssignableFrom(typeof(T))) { var dim2CodeProp = Expression.Property(parameter, nameof(IDim2.Dim2Code)); conditionExpressions.Add(Expression.Equal(dim2CodeProp, Expression.Constant(code))); } // 若T实现了IDim3接口,追加Dim3Code的匹配判断 if (typeof(IDim3).IsAssignableFrom(typeof(T))) { var dim3CodeProp = Expression.Property(parameter, nameof(IDim3.Dim3Code)); conditionExpressions.Add(Expression.Equal(dim3CodeProp, Expression.Constant(code))); } // 将所有判断条件用OR逻辑拼接 var combinedCondition = conditionExpressions.Aggregate(Expression.OrElse); // 生成完整的查询Lambda var matchLambda = Expression.Lambda<Func<T, bool>>(combinedCondition, parameter); // 直接执行查询,无需任何类型转换 return dbcontext.Set<T>().Any(matchLambda); }
方案说明
- 全程没有强制类型转换操作,所有属性访问都是直接针对泛型参数
T生成,完全符合EF的表达式解析规则,不会抛出类型转换相关报错 - 扩展性强,后续如果要新增4、5级编码,只需要对应追加
IDim4、IDim5的判断分支即可 - 生成的SQL和你原本预期的硬写逻辑完全等价,没有额外性能损耗
内容的提问来源于stack exchange,提问作者Jerker Pihl
相关产品推荐
相关产品推荐

