当我有以下 EF 查询时,无法从数据库检索数据。
var results = await (from a in agreements
let showPasscode = <snipped>
let passcode = <snipped>
select new Result(
a.Id,
a.UserId,
a.CurrentCycle().StartsOn,
passcode,
showPasscode)
)
.ToListAsync(cancellationToken);
以及
CurrentCyle
的代码
public static AgreementCycle CurrentCycle(this Agreement agreement) =>
agreement.Cycles
.OrderByDescending(x => x.StartsOn)
.FirstOrDefault();
现在,当我在扩展方法中运行它(并放置断点)时,我得到以下结果:
现在我知道数据库-确实-具有该项目的链接数据。
我猜测这可能与一些延迟表达式处理有关?就像,扩展方法在执行数据库结果之后运行并且属性尚未“包含”或其他什么?
如果我手动添加扩展方法的内容,我会得到正确的结果:
var results = await (from a in agreements
let showPasscode = <snipped>
let passcode = <snipped>
select new Result(
a.Id,
a.UserId,
a.Cycles.OrderByDescending(c => c.StartsOn).FirstOrDefault().StartsOn, // <-- HERE
passcode,
showPasscode)
)
.ToListAsync(cancellationToken);
所以我不确定需要做什么。现在,如果这都是关于缺少“Include(..)”语句的问题..这可能不起作用,因为请注意我的查询的格式 - 它就像旧的 linq-to-sql 格式。为什么?因为那些 2x
let
命令。我喜欢这些,它确实帮助我使我的查询更容易/更简单。我不确定我能以任何方式做到这一点吗?
您是否启用了客户端评估,并且您是否看到任何有关客户端评估与原始查询一起使用的警告?
扩展方法通常不适用于 EF,除非您打开了客户端评估,因为扩展方法无法转换为 SQL。我怀疑这是一种情况,您正在运行客户端评估,但禁用了延迟加载,因此当客户端针对 Cycles 执行时,集合为空。
这可能可以通过急切加载周期 /w
Include(x => x.Cycles)
来解决,但这通常并不理想,因为它会扩大返回的数据足迹大小。这也可以通过启用延迟加载来解决,但这几乎不是一个好的解决方案,因为使用集合时会影响性能。
您所做的手动提取确实是正确的解决方案。如果您想要一种更简洁的方式在一个地方执行此操作,请考虑使用与 EF 的
IQueryable
实现配合使用的映射器。通过这种方式,您可以配置如何转换实体 -> 结果 DTO 以及映射规则(例如最新的 StartsOn)可以驻留在映射中。例如 Automapper 支持 ProjectTo<TDestination>()
。 (https://docs.automapper.org/en/stable/Queryable-Extensions.html)