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

如何实现带Where子句的泛型表达式?实体内部列表筛选泛化

泛型表达式实现实体内部列表的多语言筛选

现有实体结构

public class FoodMenuT
{
   public int Id {get;set;}
   
   public int LanguageId {get;set;}
   
   public string Name {get;set;}
}
public class FoodMenu
{
   public int Id {get;set;}
   
   public IEnumerable<FoodMenuT> FoodMenuTList {get;set;}
}

已实现的非泛型筛选方法

针对FoodMenu的非泛型多语言筛选方法如下:

public static Expression<Func<FoodMenu, IEnumerable<FoodMenuT>>> LanguageFilter()
{
    return e => e.FoodMenuT.Where(x => x.LanguageId == 1);
}

泛化需求

希望将上述方法泛化为通用版本,签名如下:

public static Expression<Func<T, IEnumerable<TT>>> LanguageFilter<T, TT>()
{
    
}

目前已编写部分代码,但无法将获取实体列表的表达式与LanguageId筛选的Where条件表达式组合起来,现有代码片段:

public static Expression<Func<T, IEnumerable<TT>>> LanguageFilter<T, TT>()
{
    var headerInfo = ServiceTool.ServiceProvider.GetService<IHeaderInfo>();

    var entity = typeof(T);
    var ttEntity = typeof(TT);

    var propertyInfo = entity.GetProperty(ttEntity.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
    var arg = Expression.Parameter(entity, "e");
    var property = Expression.Property(arg, propertyInfo!.Name);

    // e=>e.FoodMenuT
    var selector = Expression.Lambda(property, new ParameterExpression[] { arg });

    var propertyInfo2 = ttEntity.GetProperty("LanguageId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
    var arg2 = Expression.Parameter(ttEntity, "x");
    var property2 = Expression.Property(arg2, propertyInfo2!.Name);
    var languageId = Expression.Constant(headerInfo!.LanguageId);
    var equalLanguage = Expression.Equal(property2, languageId);

    // x=>x.LanguageId==1
    var selector2 = Expression.Lambda<Func<TT, bool>>(equalLanguage, arg2);

    var lambda = Expression.Lambda<Func<T, IEnumerable<TT>>>(null, arg);
    return lambda;
}

解决方案

要完成泛型方法,核心是构造调用Enumerable.Where方法的表达式,将列表属性和筛选条件组合起来。完整实现如下:

public static Expression<Func<T, IEnumerable<TT>>> LanguageFilter<T, TT>()
{
    var headerInfo = ServiceTool.ServiceProvider.GetService<IHeaderInfo>();
    if (headerInfo == null)
        throw new InvalidOperationException("IHeaderInfo service not available.");

    var tType = typeof(T);
    var ttType = typeof(TT);

    // 获取T中对应TT列表的属性(按命名约定:TT类名作为属性名,可根据实际情况调整逻辑)
    var listProperty = tType.GetProperty(ttType.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
    if (listProperty == null)
        throw new ArgumentException($"Type {tType.Name} does not have a public property named {ttType.Name}.");

    // 构建参数e => ...
    var param = Expression.Parameter(tType, "e");
    // 获取e.FoodMenuT的表达式
    var listExpr = Expression.Property(param, listProperty);

    // 构建x => x.LanguageId == headerInfo.LanguageId的表达式
    var ttParam = Expression.Parameter(ttType, "x");
    var languageIdProp = ttType.GetProperty("LanguageId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
    if (languageIdProp == null)
        throw new ArgumentException($"Type {ttType.Name} does not have a public LanguageId property.");
    
    var languageIdExpr = Expression.Property(ttParam, languageIdProp);
    var constantLanguageId = Expression.Constant(headerInfo.LanguageId);
    var equalExpr = Expression.Equal(languageIdExpr, constantLanguageId);
    var filterLambda = Expression.Lambda<Func<TT, bool>>(equalExpr, ttParam);

    // 获取Enumerable.Where方法(针对IEnumerable<TT>的泛型方法)
    var whereMethod = typeof(Enumerable).GetMethods()
        .Where(m => m.Name == "Where" && m.GetParameters().Length == 2)
        .First()
        .MakeGenericMethod(ttType);

    // 构建e.FoodMenuT.Where(x => x.LanguageId == ...)的表达式
    var whereCall = Expression.Call(whereMethod, listExpr, filterLambda);

    // 最终组合成完整的Lambda表达式
    return Expression.Lambda<Func<T, IEnumerable<TT>>>(whereCall, param);
}

关键说明

  1. 属性查找逻辑:当前代码按TT类名查找T中的对应列表属性,若你的实际命名规则不同(比如属性名是{TT类名}List),需要调整GetProperty的参数为$"{ttType.Name}List"。
  2. 方法调用构造:通过反射获取Enumerable.Where的泛型方法,再用Expression.Call将列表表达式和筛选Lambda组合起来,这是实现泛型筛选的核心步骤。
  3. 异常处理:增加了必要的空值检查,避免因服务未注册、属性不存在等情况导致运行时错误。

内容的提问来源于stack exchange,提问作者Baris Yerlikaya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 16:40:22