如何在LINQ查询不同层级导航属性场景下复用同一Where条件表达式
解决方案
这里提供两种成熟的复用方案,你可以根据自己的业务场景选择:
方案1:表达式树组合(推荐,逻辑仅需定义一次)
核心思路是将公共筛选逻辑定义为针对Child的表达式树,再通过通用扩展方法把该表达式适配为任意持有Child属性的实体的筛选条件,全程只需要维护一份筛选逻辑。
第一步:定义公共筛选表达式
using System.Linq.Expressions; public static class FilterDefinitions { // 公共筛选逻辑,仅需在此处维护一次 public static Expression<Func<Child, bool>> FilterChildByProperty1(string targetValue) { return c => c.Property_1 == targetValue; } }
第二步:实现通用表达式适配扩展方法
public static class ExpressionCombineExtensions { /// <summary> /// 把针对属性的筛选表达式,适配为针对父实体的筛选表达式 /// </summary> /// <typeparam name="TParent">父实体类型</typeparam> /// <typeparam name="TChild">属性类型</typeparam> /// <param name="childFilter">针对属性的筛选表达式</param> /// <param name="propSelector">父实体取属性的选择器</param> public static Expression<Func<TParent, bool>> AdaptForParent<TParent, TChild>( this Expression<Func<TChild, bool>> childFilter, Expression<Func<TParent, TChild>> propSelector) { var parentParam = Expression.Parameter(typeof(TParent), "p"); var getChildExpr = Expression.Invoke(propSelector, parentParam); var filterBody = Expression.Invoke(childFilter, getChildExpr); return Expression.Lambda<Func<TParent, bool>>(filterBody, parentParam); } }
第三步:两种场景直接复用
// 1. 查询Child场景 var children = _context.Child .Where(FilterDefinitions.FilterChildByProperty1("X")) .ToList(); // 2. 查询Parent场景 var parents = _context.Parent .Where(FilterDefinitions.FilterChildByProperty1("X").AdaptForParent<Parent, Child>(p => p.Child)) .ToList();
方案2:IQueryable扩展方法重载(更直观,适合简单筛选逻辑)
如果你的筛选逻辑非常简单,不想处理表达式树改写,也可以通过重载扩展方法的方式实现调用侧的复用,只是筛选逻辑需要写两次,修改时要同步更新两处:
public static class QueryExtensions { public static IQueryable<Child> WhereProperty1Is(this IQueryable<Child> query, string value) { return query.Where(c => c.Property_1 == value); } public static IQueryable<Parent> WhereChildProperty1Is(this IQueryable<Parent> query, string value) { return query.Where(p => p.Child.Property_1 == value); } }
调用示例:
var children = _context.Child.WhereProperty1Is("X").ToList(); var parents = _context.Parent.WhereChildProperty1Is("X").ToList();
注意事项
如果在旧版本EF中遇到表达式翻译失败的问题,可以引入LINQKit库,在查询前调用AsExpandable()展开表达式树即可正常生成SQL:
var parents = _context.Parent .AsExpandable() .Where(FilterDefinitions.FilterChildByProperty1("X").AdaptForParent<Parent, Child>(p => p.Child)) .ToList();
内容的提问来源于stack exchange,提问作者Pawan Nogariya
相关产品推荐
相关产品推荐

