如何使用System.Linq.Expressions.Expression来过滤基于孩子的?

时间:2012-04-27 19:44:43

标签: c# .net linq-to-sql expression

我有一个用于多种方法的过滤器:

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));

但表达式不能用作方法

6 个答案:

答案 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。