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

问题描述 投票:10回答:6

我有一个过滤器,我在许多方法中使用:

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

但表达式不能用作方法

c# .net linq-to-sql expression
6个回答
4
投票

如果你想组合表达式并仍然能够使用linq-to-sql,你可能想看看LinqKit。它遍历表达式并在sql转换之前用其内容替换所有函数调用。

这样你就可以直接使用了

return db.Parents
       .AsExpandable()
       .Where(parent => parent.Status == 1 && filter(parent.Child));

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关系。

我不认为这对你的情况有用,因为涉及的类型不同,但为了以防万一,这里有一个如何组合Expressions的例子:How do I combine LINQ expressions into one?

编辑:我之前曾建议使用Compile(),但这不适用于LINQ-to-SQL。


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;

可能不是最优雅,但可能比其他解决方案更容易维护


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

希望这可以帮助!


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年多了,我还在评论这不起作用。从卫生的角度来看,我不确定是否最好只删除答案,或添加此编辑并让答案代表一些明显不起作用的例子。我很乐意就此提出建议。


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。

© www.soinside.com 2019 - 2024. All rights reserved.