将Linq转换为实体聚合

时间:2015-11-12 19:48:47

标签: c# linq entity-framework-6

使用Entity Framework 6,我正在寻找相当于Aggregate - 带有种子的重载 - 但是从种子延伸而不是从聚合集合延伸。也就是说,给定Expression<Func<T, bool>>的集合(适用于Where),我想将它们连续应用于基础IQueryable

注意:即使Linq to Entities本身不支持Aggregate,但诀窍是我们不会尝试将Aggregate操作投射到SQL中,我们正在使用{{1}方法IEnumerable将条件添加到Aggregate。因此,只要IQueryable是种子,Aggregate就可以与EF一起使用,而不是扩展方法的IQueryable参数。

所以给出了这个设置:

this

而不是:

public class Product {
    public int Name { get; set; }
    public int Size { get; set; }
    public int Price { get; set; }
}

var products = new List<Product> {
    new Product { Name = "toaster", Size = 100, Price = 49 },
    new Product { Name = "pencil", Size = 2, Price = 5 },
    new Product { Name = "Oven", Size = 500, Price = 1000 }
}.AsEnumerable(); // please pretend this is an `IQueryable`

var conditions = new List<Func<Product, bool>> {
    { o => o.Size > 50 },
    { o => o.Price < 100 }
}.AsEnumerable(); // please pretend this is of Expression<Func<Product, bool>>

我想改为能够使用:

IQueryable<Product> filtered =
   conditions.Aggregate(queryable, (current, condition) => current.Where(condition));

这是我必须编写自己的扩展方法,还是有办法用本机Linq to Entities扩展方法实现这一点?我认为IQueryable<Product> filtered = queryable.WhereAll(conditions); 语法在没有发生真正聚合的情况下会令人困惑(至少,开发人员会如何思考)。如果我确实使用Aggregate,我认为在代码中会出现令人惊讶或混乱,首先是因为没有真正的聚合发生(例如求和或连接),其次是因为当Aggregate是一个空集合时,它仍然返回种子(使用可查询/集合和扩展方法的一些非惯用和令人惊讶的结果,其中返回值不是扩展方法操作的对象的转换)。

我愿意接受比conditions更好的名字。我考虑过WhereAllTransformMap

如果仅适用于Apply和Linq to Objects,the answer is easy

IEnumerables

但是,对于Linq to Entities,无法将其投影到SQL中,因为您无法手动处理lambda中的表达式以将表达式正确添加到父表达式树中(表达式本身必须是整个lambda),除非你想对products.Where(p => conditions.All(c => c(p))); 的表达式进行复杂的手术(对此过度杀伤)。

1 个答案:

答案 0 :(得分:0)

编写扩展方法很简单..

public static IQueryable<T> WhereAll<T>(this IQueryable<T> q,IEnumerable<Expression<Func<T, bool>>> conditions)
{
  foreach(var condition in conditions)
    q=q.Where(condition);
  return q;
}

您也可以使用PredicateBuilder。在所有条件下初始化为True和AND,然后只使用常规.Where和您构建的复合谓词。