以下实体框架查询运行时没有错误。
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 表达式节点类型"调用"在 LINQ to Entities 中不受支持。
我期望 LINQ 能够将我的谓词合并到查询中并将其转换为 SQL。为什么我会收到此错误,有没有办法以这种方式动态修改 where 谓词?
导致此问题的原因是实体框架需要能够将 LINQ 查询转换为 SQL。LINQ 查询将编译为称为表达式树的数据结构,然后将其传递给实体框架以转换为 SQL。
您有两种选择:
- 将
filterProgram
调用替换为更基本的 C# 表达式。根据filterProgram
的复杂性,这可能是不可能的。 - 删除对
filterProgram
的调用,并将此查询转换为IEnumerable<T>
,也许通过调用.ToList()
。然后,可以使用filterProgram
进一步筛选此查询的结果
示例 2
var query = /* Your Query With filterProgram commented out */
var resultsFromSql = query.ToList();
var fullyFiltered = resultsFromSql.Select(filterProgram);
将filterProgram
的类型更改为Expression<Func<Program, bool>>
,然后它应该可以在 LINQ Where 子句中使用。
但是,有一个警告:我设法让它在方法链语法中工作,而不是在查询语法中工作。
例如,这适用于:
dataContext.Programs.Where (filterProgram)
但这不会:
from p in dataContext.Programs
where filterprogram
(编译器抱怨它无法解析方法Where
,该方法在IEnumerable
和IQueryable
上都可用。
在您的情况下,更换该行可能是可以接受的
join p in repository.Query<Program>()
跟
join p in repository.Query<Program>().Where(filterProgram)
在 Linq to Entities 中,如果不将查询转换为 IEnumerable
,则无法调用外部方法。因此,由于filterProgram
是一个方法,因此不能在查询中调用。
如果可能,您可以在拨打FilterProgram
之前致电ToList
,它将起作用。另一种选择可能是将filterProgram
逻辑插入查询中。
问题是 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 是无效的程序 ID