我有一个过滤器,我在许多方法中使用:
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
s的例子:How do I combine LINQ expressions into one?
编辑:我之前曾建议使用Compile()
,但这不适用于LINQ-to-SQL。
好吧,如果父母和孩子之间存在1:1的关系(不太可能,但示例似乎暗示了这一点),那么你可以这样做:
return db.Parents
.Where(parent => parent.Status == 1)
.Select(parent => parent.Child)
.Where(filter)
.Select(child=> child.Parent);
否则会很难。
你可以用dynamic linq做到这一点,但这可能是矫枉过正。
你可以generate your expression tree manually,但这也很复杂。我自己没试过。
作为最后的手段,你当然可以总是调用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))
就个人而言,我只是将表达式定义两次,一次是针对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。