如何使用 System.Linq.Expressions.Expression 基于子项进行筛选
本文关键字:筛选 System 何使用 Linq Expressions Expression | 更新日期: 2023-09-27 18:32:43
我有一个过滤器,我可以在许多方法中使用:
Expression<Func<Child, bool>> filter = child => child.Status == 1;
(实际上比这更复杂)
我必须执行以下操作
return db.Parents.Where(parent => parent.Status == 1 &&
parent.Child.Status == 1);
条件与上述过滤器中的条件相同。
我想在此方法中重用过滤器。但我不知道怎么做。我试过了
return db.Parents.Where(parent => parent.Status == 1 &&
filter(parent.Child));
但表达式不能用作方法
如果你想组合表达式,并且仍然能够使用linq-to-sql,你可能想看看LinqKit。它会进入表达式内部,并在 sql 转换之前用它们的内容替换所有函数调用。
这样您就可以直接使用
return db.Parents
.AsExpandable()
.Where(parent => parent.Status == 1 && filter(parent.Child));
你可以试试这个:
var compiledFilter = filter.Compile();
foreach (var parent in db.Parents.Where(parent => parent.Status == 1))
if (compiledFilter(parent.Child))
yield return parent;
它要求您拉动所有父项,但与@HugoRune的解决方案不同,它不需要父项:子项的 1:1 关系。
由于涉及不同的类型,我认为这对您的情况没有用,但以防万一,下面是如何将Expression
组合的示例:如何将 LINQ 表达式合并为一个?
编辑:我之前建议使用Compile()
,但这不适用于LINQ-to-SQL。
好吧,如果父子之间存在 1:1 的关系(不太可能,但该示例似乎暗示了这一点)然后您可以这样做:
return db.Parents
.Where(parent => parent.Status == 1)
.Select(parent => parent.Child)
.Where(filter)
.Select(child=> child.Parent);
否则会很难。
您可以使用动态 linq 来做到这一点,但这可能是矫枉过正。
您可以手动生成表达式树,但这也非常复杂。我自己没有尝试过。
作为最后的手段,你当然可以随时调用yourQuery.AsEnumerable()
,这将导致linq-to-sql到目前为止将你的查询转换为sql,并在客户端执行其余的工作;然后你可以.compile()你的表达式。但是,你失去了linq-to-sql的性能优势(并且compile()本身非常慢;每当它被执行时,它都会调用JIT编译器):
return db.Parents
.Where(parent => parent.Status == 1)
.AsEnumerable()
.Where(parent => filter.Compile().Invoke(parent.Child))
就我个人而言,我只定义两次表达式,一次用于孩子,一次用于 parent.child:
Expression<Func<Child, bool>> filterChild = child => child.Status == 1;
Expression<Func<Parent, bool>> filterParent = parent => parent.Child.Status == 1;
可能不是最优雅的,但可能比其他解决方案更容易维护
想出这个,检查这是否适合你
public interface IStatus { public int Status { get; set; } }
public class Child : IStatus { }
public class Parent : IStatus
{public Child Child { get; set; } }
Func<IStatus, bool> filter = (x) => x.Status == 1;
var list = Parents.Where(parent => filter(parent) && filter(parent.Child));
希望这有帮助!
你能把表达式作为一个函数来代替吗?
而不是:
Expression<Func<Child, bool>> filter = child => child.Status == 1;
以这种方式使用与泛型函数相同的表达式:
Func<Child, bool> filter = child => child.Status == 1;
然后,您将能够以与尝试使用表达式相同的方式使用该函数:
return db.Parents.Where(parent => parent.Status == 1 &&
filter(parent.Child));
编辑:我误解了这个问题。这是一个糟糕的答案。6+ 年后,我仍然收到评论说这不起作用。从卫生的角度来看,我不确定是否最好删除答案,或者添加此编辑并让答案作为绝对不起作用的示例。我愿意接受这方面的建议。
不需要外部库或使用表达式树。 相反,请编写 lambda 函数以使用查询链接并利用 LINQ 的延迟执行。
而不是:
Expression<Func<Child, bool>> filter = child => child.Status == 1;
将其重写为:
Func<IQueryable<Parent>, IQueryable<Parent>> applyFilterOnParent = query => query.Where(parent => parent.Child.Status == 1);
Func<IQueryable<Child>, IQueryable<Child>> applyFilterOnChild = query => query.Where(child => child.Status == 1);
现在,而不是:
return db.Parents.Where(parent => parent.Status == 1 &&
filter(parent.Child));
你可以写:
var query = db.Parents.AsQueryable();
query = applyFilterOnParent(query);
return query.Where(parent => parent.Status == 1);
您可以在其他 LINQ 查询中重复使用 applyFilter 函数。 当您希望将 lambda 函数与 LINQ-to-SQL 一起使用时,此技术非常有效,因为 LINQ 不会将 lambda 函数转换为 SQL。