C# 如何使用 System.Linq.Expressions.Expression 基于子项进行过滤?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10356889/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 13:32:13  来源:igfitidea点击:

How to use System.Linq.Expressions.Expression to filter based on children?

c#.netlinq-to-sqlexpression

提问by Jader Dias

I have a filter that I use across many methods:

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

Expression<Func<Child, bool>> filter = child => child.Status == 1;

(actually is more complex than that)

(实际上比这更复杂)

And I have to do the following

我必须执行以下操作

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

where the condition is the same as in the filter above.

其中条件与上面的过滤器相同。

I want to reuse the filter in this method. But I don't know how. I tried

我想在这种方法中重用过滤器。但我不知道怎么做。我试过

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

but an Expression can't be used as a method

但表达式不能用作方法

采纳答案by McX

If you want to combine expressions and still be able to use linq-to-sql, you may want to have a look at LinqKit. It walks inside your expression and replaces all the function calls by their contents before the sql conversion.

如果您想组合表达式并且仍然能够使用 linq-to-sql,您可能需要查看LinqKit。它会进入您的表达式,并在 sql 转换之前将所有函数调用替换为其内容。

This way you'll be able to use directly

这样你就可以直接使用

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

回答by Tim S.

You can try this:

你可以试试这个:

var compiledFilter = filter.Compile();
foreach (var parent in db.Parents.Where(parent => parent.Status == 1))
    if (compiledFilter(parent.Child))
        yield return parent;

It requires you to pull all of the parents, but unlike @HugoRune's solution, it doesn't require a 1:1 relation of Parent:Child.

它需要你拉动所有的父母,但与@HugoRune 的解决方案不同,它不需要 Parent:Child 的 1:1 关系。

I don't think this will be useful for your situation because of the different types involved, but just in case, here is an example of how you can combine Expressions: How do I combine LINQ expressions into one?

由于涉及的类型不同,我认为这对您的情况没有用,但为了以防万一,这里有一个如何组合Expressions的示例:如何将 LINQ 表达式组合成一个?

Edit: I had previously suggested using Compile(), but that doesn't work over LINQ-to-SQL.

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

回答by HugoRune

Well, if there is a 1:1 relationship between parent and child (unlikely, but the example seems to imply that) then you could do it like this:

好吧,如果父母和孩子之间存在 1:1 的关系(不太可能,但示例似乎暗示了这一点),那么您可以这样做:

  return db.Parents
  .Where(parent => parent.Status == 1)
  .Select(parent => parent.Child)
  .Where(filter)
  .Select(child=> child.Parent);

Otherwise it will be hard.

否则会很辛苦。

You could do it with dynamic linqbut that is probably overkill.

您可以使用动态 linq来做到这一点,但这可能有点矫枉过正。

You could generate your expression tree manually, but that is also quite complicated. I have not tried that myself.

您可以手动生成表达式树,但这也非常复杂。我自己没有尝试过。

As a last resort you could of course always call yourQuery.AsEnumerable(), this will cause linq-to-sql to translate your query into sql up to this point and perform the rest of the work on the client-side; then you can .compile() your expression. However you lose the performance benefits of linq-to-sql (and compile() itself is quite slow; whenever it is executed, it calls the JIT-compiler):

作为最后的手段,您当然可以始终调用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))

Personally I'd just define the expression twice, once for child and once for parent.child:

就我个人而言,我只是定义了两次表达式,一次为 child,一次为 parent.child:

   Expression<Func<Child, bool>> filterChild = child => child.Status == 1;
   Expression<Func<Parent, bool>> filterParent = parent => parent.Child.Status == 1;

Might not be the most elegant, but probably easier to maintain than the other solutions

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

回答by Eduardo Crimi

Just come up with this, check if this would work for you

想出这个,看看这是否适合你

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

Hope this helps!

希望这可以帮助!

回答by jdmcnair

Could you just use the expression as a function instead?

您可以将表达式用作函数吗?

Instead of:

代替:

Expression<Func<Child, bool>> filter = child => child.Status == 1;

Use that same expression as a generic function this way:

以这种方式使用与泛型函数相同的表达式:

Func<Child, bool> filter = child => child.Status == 1;

Then you will be able to use the function in just the same way you were trying to use an expression:

然后,您将能够以与尝试使用表达式相同的方式使用该函数:

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

Edit:I misunderstood the question. This is a bad answer. 6+ years out, I'm still getting comments to the effect that this doesn't work. I'm not sure, from a hygiene perspective, if it would be better to just delete the answer, or add this edit and let the answer stand as an example of something that decidedly doesn't work. I'm open to advisement on that.

编辑:我误解了这个问题。这是一个糟糕的答案。6 年多过去了,我仍然收到评论说这不起作用。我不确定,从卫生的角度来看,删除答案是否更好,或者添加此编辑并让答案作为一个明显不起作用的例子。我愿意接受这方面的建议。

回答by humbads

There's no need for external libraries or mucking around with expression trees. Instead, write your lambda functions to use query chaining and take advantage of LINQ's deferred execution.

不需要外部库或处理表达式树。相反,编写 lambda 函数以使用查询链并利用 LINQ 的延迟执行。

Instead of:

代替:

Expression<Func<Child, bool>> filter = child => child.Status == 1;

Expression<Func<Child, bool>> filter = child => child.Status == 1;

Rewrite it as:

将其改写为:

Func<IQueryable<Parent>, IQueryable<Parent>> applyFilterOnParent = query => query.Where(parent => parent.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);

Func<IQueryable<Child>, IQueryable<Child>> applyFilterOnChild = query => query.Where(child => child.Status == 1);

Now, instead of:

现在,而不是:

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

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

You can write:

你可以写:

var query = db.Parents.AsQueryable(); query = applyFilterOnParent(query); return query.Where(parent => parent.Status == 1);

var query = db.Parents.AsQueryable(); query = applyFilterOnParent(query); return query.Where(parent => parent.Status == 1);

And you can re-use the applyFilter functions in other LINQ queries. This technique works well when you want to use lambda functions together with LINQ-to-SQL, because LINQ will not translate a lambda function to SQL.

并且您可以在其他 LINQ 查询中重用 applyFilter 函数。当您希望将 lambda 函数与 LINQ-to-SQL 一起使用时,此技术非常有效,因为 LINQ 不会将 lambda 函数转换为 SQL。