7

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

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

4 回答 4

3

问题是因为实体框架需要能够将您的 LINQ 查询转换为 SQL。您的 LINQ 查询被编译成称为表达式树的数据结构,然后将其传递给实体框架以转换为 SQL。

你有两个选择:

  1. filterProgram用更基本的 C# 表达式替换您的调用。根据复杂性,filterProgram这可能是不可能的。
  2. 删除您对 的调用filterProgram,并将此查询转换为IEnumerable<T>,也许通过调用.ToList()。然后,您可以使用进一步过滤此查询的结果filterProgram

示例 2

   var query = /* Your Query With filterProgram commented out */
   var resultsFromSql = query.ToList();
   var fullyFiltered = resultsFromSql.Select(filterProgram);
于 2013-01-30T07:49:10.000 回答
2

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

然而,有一个警告:我设法让它在方法链语法中工作,但不是在查询语法中。

例如,这有效:

dataContext.Programs.Where (filterProgram)

但这不会:

from p in dataContext.Programs
where filterprogram 

(编译器抱怨它无法解析方法,该方法在和Where上都可用。)IEnumerableIQueryable

在您的情况下,更换该行可能是可以接受的

join p in repository.Query<Program>()

join p in repository.Query<Program>().Where(filterProgram)
于 2013-01-30T08:06:58.510 回答
1

在 Linq to Entities 中,如果不将查询转换为IEnumerable. 因此,既然filterProgram是一个方法,你就不能在查询内部调用。

如果可能的话,你可以在打电话ToList之前打电话FilterProgram,它会起作用。另一种选择可能是将逻辑插入filterProgram到查询中。

于 2013-01-30T07:38:34.940 回答
1

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

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

于 2013-01-30T08:07:08.300 回答