动态修改LINQ查询中的Where条件

时间:2013-01-30 07:26:42

标签: c# linq entity-framework predicate

以下实体框架查询运行时没有错误。

Predicate<Program> filterProgram;
if (programId.HasValue)
    filterProgram = (p => p.Id == programId && !p.IsDeleted);
else
    filterProgram = (p => !p.IsDeleted);

var analytics = (from a in repository.Query<Analytic>()
                 where (a.Marker == "Open" || a.Marker == "LastTouch") &&
                 a.EntityType == "Proposal" &&
                 a.Site == "C"
                 join p in repository.Query<Program>()
                 on a.EntityId equals p.Id
                 //where filterProgram(p)
                 group a
                 by new { a.LoginSessionId, a.EntityId, p.Id, p.Name } into g
                 let f = g.OrderBy(x => x.TimestampUtc).FirstOrDefault(x => x.Marker == "Open")
                 where f != null
                 let t = g.FirstOrDefault(x => x.Marker == "LastTouch" && x.TimestampUtc > f.TimestampUtc)
                 select new
                 {
                     ProgramId = g.Key.Id,
                     Program = g.Key.Name,
                     ProposalId = g.Key.EntityId,
                     FirstOpen = f,
                     LastTouch = (t ?? f).TimestampUtc
                 }).ToList();

但是,如果我取消注释行where filterProgram(p),我会收到运行时错误:

  

LINQ to Entities中不支持LINQ表达式节点类型“Invoke”。

我期待LINQ能够将我的谓词合并到查询中并将其转换为SQL。为什么我会收到此错误,是否有办法以这种方式动态修改where谓词?

4 个答案:

答案 0 :(得分:3)

问题是由于Entity Framework需要能够将LINQ查询转换为SQL而引起的。您的LINQ查询被编译为称为表达式树的数据结构,然后将其传递给Entity Framework以转换为SQL。

您有两种选择:

  1. 使用更基本的C#表达式将您的呼叫替换为filterProgram。根据{{​​1}}的复杂程度,这可能是不可能的。
  2. 移除您对filterProgram的来电,并将此查询转换为filterProgram,也许可以致电IEnumerable<T>。然后,您可以使用.ToList()
  3. 进一步过滤此查询的结果

    2

    的例子
    filterProgram

答案 1 :(得分:2)

filterProgram的类型更改为Expression<Func<Program, bool>>,然后它应该可以在LINQ Where子句中使用。

但有一点需要注意:我设法让它在方法链语法中工作,但在查询语法中没有。

例如,这有效:

dataContext.Programs.Where (filterProgram)

但这不是:

from p in dataContext.Programs
where filterprogram 

(编译器抱怨它无法解析WhereIEnumerable上提供的方法IQueryable。)

在您的情况下,替换行

可能是可以接受的
join p in repository.Query<Program>()

join p in repository.Query<Program>().Where(filterProgram)

答案 2 :(得分:1)

在Linq to Entities中,如果不将查询转换为IEnumerable,则无法调用外部方法。因此,由于filterProgram是一种方法,因此您不能在查询中调用。

如果可能,您可以在致电ToList之前致电FilterProgram,这样就可以了。 另一种选择可能是将filterProgram的逻辑插入到查询中。

答案 3 :(得分:1)

问题是linq2entities尝试将表达式树转换为SQL,表达式树在委托类型(filterProgram)上调用方法Invoke 但是没有什么可以阻止你内联谓词

var id= programId.HasValue ? programId.GetValueOrDefault() : -1;
var analytics = (from a in repository.Query<Analytic>()
                 where (a.Marker == "Open" || a.Marker == "LastTouch") &&
                 a.EntityType == "Proposal" &&
                 a.Site == "C"
                 join p in repository.Query<Program>()
                 on a.EntityId equals p.Id
                 where !p.IsDeleted && (!hasValue || p.Id == id)
                 ....

这假设-1是无效的programId