如何在DbContext中使用Dynamic LINQ筛选派生类属性
问题描述
- 基类
A包含Id、Created_at、Deleted_at等通用属性 - 存在
Subclass_B、Subclass_C、Subclass_D等多个派生类,每个派生类拥有专属属性(如Subclass_B.Name、Subclass_C.Lastname等) - 当前通过键值对形式的筛选条件查询数据库时,筛选基类
A的属性可正常返回结果,但筛选派生类专属属性时,会抛出异常:System.Linq.Dynamic……'No property or field 'MyProperty' exists in type 'A'' - 无法提前知晓要筛选的派生类类型,需实现遍历所有
A的派生类并匹配筛选条件的功能
当前实现代码
public async Task<List<A>> Filter(JObject criteria) { var query = _context.A.AsQueryable(); foreach (KeyValuePair<string, JToken> filter in criteria) { var filterExpression = $"{filter.Key} == "; query = query.Where(filterExpression, filter.Value.ToString()); } return await query.ToListAsync(); }
筛选条件示例JSON
{ "Id": "446" }
解决方案
核心问题在于初始查询的根类型是基类A,动态LINQ无法识别派生类的专属属性。需针对每个派生类单独构建筛选查询,再合并所有结果。
1. 获取所有A的派生类
通过反射获取程序集中所有继承自A的非抽象类:
var derivedTypes = Assembly.GetAssembly(typeof(A)) .GetTypes() .Where(t => t.IsSubclassOf(typeof(A)) && !t.IsAbstract);
2. 为每个派生类构建筛选逻辑
遍历每个派生类,将基类查询转为派生类查询后应用筛选条件,最后合并结果:
public async Task<List<A>> Filter(JObject criteria) { var allResults = new List<A>(); var derivedTypes = Assembly.GetAssembly(typeof(A)) .GetTypes() .Where(t => t.IsSubclassOf(typeof(A)) && !t.IsAbstract); foreach (var type in derivedTypes) { // 将基类查询转为当前派生类的查询 var ofTypeMethod = typeof(Queryable).GetMethod(nameof(Queryable.OfType)) .MakeGenericMethod(type); var derivedQuery = (IQueryable)ofTypeMethod.Invoke(null, new object[] { _context.A }); // 逐个应用筛选条件 foreach (var filter in criteria) { // 检查当前派生类是否包含该属性,不存在则跳过 var property = type.GetProperty(filter.Key); if (property == null) continue; // 构建动态LINQ筛选表达式并应用 var filterExpr = $"{filter.Key} == @0"; var whereMethod = typeof(Queryable).GetMethods() .First(m => m.Name == nameof(Queryable.Where) && m.GetParameters().Length == 2) .MakeGenericMethod(type); derivedQuery = (IQueryable)whereMethod.Invoke(null, new object[] { derivedQuery, filterExpr, filter.Value.ToString() }); } // 执行查询并合并结果 var toListAsyncMethod = typeof(EntityFrameworkQueryableExtensions).GetMethods() .First(m => m.Name == nameof(EntityFrameworkQueryableExtensions.ToListAsync) && m.GetParameters().Length == 1) .MakeGenericMethod(type); var derivedResults = await (Task<IEnumerable>)toListAsyncMethod.Invoke(null, new object[] { derivedQuery }); allResults.AddRange(derivedResults.Cast<A>()); } return allResults; }
3. 优化建议
- 缓存派生类类型:将
derivedTypes的结果缓存起来,避免每次查询都执行反射操作 - 类型适配:根据属性类型调整筛选参数值(比如数值类型无需加引号),避免类型不匹配问题
- 扩展运算符支持:如果需要
Contains、>等多运算符筛选,可扩展表达式构建逻辑,根据条件键的前缀/后缀判断运算符
注意事项
- 确保EF Core已正确配置继承映射(如TPH/TPT/TPC),否则无法正确查询派生类数据
- 动态LINQ的表达式语法需与属性类型匹配,避免因类型不兼容导致的异常
内容的提问来源于stack exchange,提问作者joadm
相关产品推荐
相关产品推荐

