You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 16:05:19