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



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

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。

您有两种选择:

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

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)

在 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

相关内容

  • 没有找到相关文章

最新更新