12

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

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

但表达式不能用作方法

4

6 回答 6

4

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

这样你就可以直接使用

return db.Parents
       .AsExpandable()
       .Where(parent => parent.Status == 1 && filter(parent.Child));
于 2012-05-07T18:11:21.890 回答
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 的示例:如何将 LINQ 表达式组合成一个?

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

于 2012-04-27T19:51:06.057 回答
1

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

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

否则会很难。

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

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

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

就我个人而言,我只定义了两次表达式,一次用于子项,一次用于父子项:

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

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

于 2012-04-27T20:01:45.523 回答
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));

希望这可以帮助!

于 2012-04-27T20:29:24.350 回答
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 年多过去了,我仍然收到评论说这不起作用。从卫生的角度来看,我不确定是否最好删除答案,或者添加此编辑并让答案作为绝对不起作用的示例。我愿意就此提出建议。

于 2012-05-09T14:30:47.223 回答
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。

于 2019-02-19T04:23:43.933 回答