EF Core 7编译查询触发InvalidOperationException异常求助
问题描述
我正在使用EF Core 7.0.5的编译查询功能,连接Linux上的SQL Server 2019实例,应用是运行在Windows上的ASP.NET Core MVC程序。
失败的编译查询
private static readonly Func<ApplicationDbContext, Guid, List<RelatedItem>> _findRelatedPrimaryItems = EF.CompileQuery( (ApplicationDbContext database, Guid entityID) => database.RelatedItems .AsNoTracking() .Where(ri => ri.PrimaryItemID == entityID) .ToList() );
调用代码
public List<RelatedItem> FindRelatedItemsForDocument(Guid documentID) { // 这段注释的查询可正常运行,但不是我想要的编译查询 //List<RelatedItem> items = this.RelatedItems // .AsNoTracking() // .Where(ri => ri.PrimaryItemID == documentID) // .ToList(); List<RelatedItem> items = _findRelatedPrimaryItems(this, documentID); return items; }
错误信息
InvalidOperationException: LINQ表达式'DbSet().Where(r => r.PrimaryItemID == __entityID).ToList()'无法被转换。请将查询改写为可转换的形式,或通过调用'AsEnumerable'、'AsAsyncEnumerable'、'ToList'或'ToListAsync'显式切换到客户端评估。
正常运行的编译查询示例
private static readonly Func<ApplicationDbContext, Guid, Document?> _findDocument = EF.CompileQuery( (ApplicationDbContext database, Guid documentID) => database.Documents .AsNoTracking() .Where(d => d.DocumentID == documentID) .FirstOrDefault() );
相关表结构
RelatedItems表
| 列名 | 定义 |
|---|---|
| RelatedItems.RelatedItemID | PK bigint identity not null |
| RelatedItems.PrimaryItemID | uniqueidentifier not null |
| RelatedItems.SecondayItemID | uniqueidentifier not null |
Documents表
| 列名 | 定义 |
|---|---|
| Documents.DocumentID | PK uniqueidentifier not null |
| Documents.Title | nvarchar(max) not null |
两个表之间无数据库关联,看不出失败查询和正常查询的差异,求解决方法。
解决方案
这个问题是EF Core 7编译查询的已知限制:编译查询不允许直接在查询根上调用ToList(),但FirstOrDefault()这类返回单个实体的方法属于服务端可执行的终端操作,因此能正常编译。
可以通过以下两种方式修复:
- 保持服务端评估(推荐)
将编译查询返回IEnumerable<RelatedItem>,调用时再转为List<RelatedItem>:
private static readonly Func<ApplicationDbContext, Guid, IEnumerable<RelatedItem>> _findRelatedPrimaryItems = EF.CompileQuery( (ApplicationDbContext database, Guid entityID) => database.RelatedItems .AsNoTracking() .Where(ri => ri.PrimaryItemID == entityID) ); // 调用代码 public List<RelatedItem> FindRelatedItemsForDocument(Guid documentID) { return _findRelatedPrimaryItems(this, documentID).ToList(); }
- 显式切换客户端评估(不推荐,大数据量影响性能)
如果必须在编译查询内完成列表转换,可先调用AsEnumerable()切换到客户端评估,再执行ToList():
private static readonly Func<ApplicationDbContext, Guid, List<RelatedItem>> _findRelatedPrimaryItems = EF.CompileQuery( (ApplicationDbContext database, Guid entityID) => database.RelatedItems .AsNoTracking() .Where(ri => ri.PrimaryItemID == entityID) .AsEnumerable() .ToList() );
核心原因是ToList()属于触发查询执行的终端操作,会打破编译查询要求的“可转换为SQL的表达式树”规则;而FirstOrDefault()对应SQL的SELECT TOP 1,属于服务端可直接执行的逻辑,因此能通过编译。
内容的提问来源于stack exchange,提问作者Sam Axe
相关产品推荐
相关产品推荐

