Linq Select 不会在 EF Core 中检索超过 3 个子级别的对象数据

Linq Select wont retrieve beyond 3 sub levels of object data in EF Core

我已经用虚构的名称替换了对象名称,以免有人质疑我的这个数据库的业务案例很奇怪 :)

如果我将下面代码中的 FavouriteBook.Name 替换为 FavouriteBookId.ToString() 我会恢复正常,因为如果有用户,总会有一本最喜欢的书。 如果商店没有用户,以下代码也有效。但是一旦有用户.. 如果它比 Profile 更深任何级别,它似乎不想获取数据。

(Profile是user的强制对象,favorite book是Profile的强制对象。"Name"是FavouriteBook对象上的字符串属性)

var ret = _context.Shops.Where(x => x.ShopId == shopId);
var selected= ret.Select(y => new UserSummaryRow //my DTO
{
    //…other properties being set have been trimmed
    UserFavouriteBook = 
      y.User.FirstOrDefault(x => x.UserId == y.UserId) == null ? "N/A" 
    : y.User.FirstOrDefault(x => x.UserId == y.UserId).Profile.FavouriteBook.Name

});
var summary = await selected.ToListAsync();
return summary;

当执行带有 "await" 的行时,调用此存储库方法的 api 方法 returns 错误请求错误:

  • $exception {System.NullReferenceException: Object reference not set to an instance of an object. at lambda_method(Closure , Object[] ) at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.TaskLiftingExpressionVisitor.ExecuteAsync[T](IReadOnlyList1 taskFactories, Func2 selector) at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.AsyncSelectEnumerable2.AsyncSelectEnumerator.MoveNext(CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor1.EnumeratorExceptionInterceptor.MoveNext(CancellationToken cancellationToken) at System.Linq.AsyncEnumerable.Aggregate[TSource,TAccumulate,TResult](IAsyncEnumerable1 source, TAccumulate seed, Func3 accumulator, Func2 resultSelector, CancellationToken cancellationToken) at {namespace of repo method}(Int32 RecId, List1 statuses) in .cs:line 2104 at {namespace on api controller method}(String dtoString) in C:\Dev}path to repo}:line 608} System.NullReferenceException

输出显示:

Microsoft.EntityFrameworkCore.Query:Error: An exception occurred in the database while iterating the results of a query for context type '{namespace of my context}'. System.NullReferenceException: Object reference not set to an instance of an object. at lambda_method(Closure , Object[] ) at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.TaskLiftingExpressionVisitor._ExecuteAsync[T](IReadOnlyList1 taskFactories, Func2 selector) at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.AsyncSelectEnumerable2.AsyncSelectEnumerator.MoveNext(CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor1.EnumeratorExceptionInterceptor.MoveNext(CancellationToken cancellationToken)

我正在使用 .NET Core/EF core/SQL 服务器 13.0。

您引用的实体似乎没有按需加载。您是否正确定义了延迟加载?

您可以通过调用

直接加载这些引用

.Include()

所以你需要改变

_context.Shops.Include(s=> s.User.Profile.FavouriteBook)

然后你就可以做剩下的过滤和投影了。

我不能确切地说出问题所在,因为这取决于使用的 EF Core 版本 - EF Core 查询转换/处理仍然不稳定。如果您包含 EF Core 日志记录输出(如已执行 SQL 查询、客户端评估警告(如果有)等),那就太好了。

但是一般的表达方式是这样的

y.User.FirstOrDefault(x => x.UserId == y.UserId).Profile.FavouriteBook.Name

总是可疑 - 正确执行完全取决于查询提供程序对 FirstOrDefault 返回的潜在 null "object" 属性的实现。 LINQ to Objects 肯定会抛出 NRE。如果使用服务器评估,EF Core 应该能够通过返回 null 来处理它,但根据异常情况,它不是由于翻译或 client/mixed 结果评估。

话虽如此,我个人会尝试使用包含 Where + Select 的等效 "natural" 方法返回 null 最终值final 属性 和 then 应用 FirstOrDefault,例如

UserFavouriteBook = y.User
    .Where(x => x.UserId == y.UserId)
    .Select(x => x.Profile.FavouriteBook.Name)
    .FirstOrDefault() ?? "N/A"