我有一个用于多种方法的过滤器:
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));
但表达式不能用作方法
答案 0 :(得分:3)
如果要组合表达式并仍然可以使用linq-to-sql,您可能需要查看LinqKit。它遍历表达式并在sql转换之前用其内容替换所有函数调用。
通过这种方式,您可以直接使用
return db.Parents
.AsExpandable()
.Where(parent => parent.Status == 1 && filter(parent.Child));
答案 1 :(得分:1)
你可以试试这个:
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。
答案 2 :(得分:1)
好吧,如果父母和孩子之间存在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;
可能不是最优雅,但可能比其他解决方案更容易维护
答案 3 :(得分:0)
想出这个,检查这是否适合你
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));
希望这有帮助!
答案 4 :(得分:0)
您可以将表达式用作函数吗?
而不是:
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年多了,我还在评论这不起作用。从卫生的角度来看,我不确定是否最好只删除答案,或添加此编辑并让答案代表一些明显不起作用的例子。我愿意就此提出建议。
答案 5 :(得分:0)
不需要外部库,也不需要弄乱表达式树。相反,编写您的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。