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

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

在有人质疑我对此数据库的商业案例的怪异之前,我用虚构的对象名称替换了它:)

如果我将下面代码中的FavouriteBook.Name替换为FavouriteBookId.ToString(),我会得到很好的回答,因为如果有用户,总会有一本喜欢的书。
如果商店没有用户,则以下代码也适用。但是一旦有了用户,它似乎并不想获得比Profile更深入的数据。

(配置文件是用户的必需对象,喜欢的书是配置文件的必需对象。"名称"是FavouriteBook对象上的字符串属性)

1
2
3
4
5
6
7
8
9
10
11
var ret = _context.Shops.Where(x => x.ShopId == shopId);
var selected= ret.Select(y => new UserSummaryRow //my DTO
{
    //a€|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方法返回错误的错误请求:

  • $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, Func
    2 selector) at
    Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.AsyncSelectEnumerable2.AsyncSelectEnumerator.MoveNext(CancellationToken
    cancellationToken) at
    Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor
    1.EnumeratorExceptionInterceptor.MoveNext(CancellationToken
    cancellationToken) at
    System.Linq.AsyncEnumerable.Aggregate[TSource,TAccumulate,TResult](IAsyncEnumerable1
    source, TAccumulate seed, Func
    3 accumulator, Func2 resultSelector,
    CancellationToken cancellationToken) at {namespace of repo
    method}(Int32 RecId, List
    1 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, Func
2 selector) at
Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.AsyncSelectEnumerable2.AsyncSelectEnumerator.MoveNext(CancellationToken
cancellationToken) at
Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor
1.EnumeratorExceptionInterceptor.MoveNext(CancellationToken
cancellationToken)

我正在使用.NET Core / EF Core / SQL Server 13.0。


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

但是在这样的一般表达中

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

总是可疑的-正确的执行完全取决于查询提供程序实现的由FirstOrDefault返回的潜在null"对象"的属性。 LINQ to Objects肯定会抛出NRE。 EF Core(如果使用服务器评估)应该能够通过返回null来处理它,但根据例外情况,它并非由于翻译或客户端/混合结果评估而引起。

话虽如此,我个人将尝试使用等效的"自然"方式,通过使用包含最终属性的Where Select然后应用FirstOrDefault来返回null最终值,例如<铅>

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


看来您引用的实体未按需加载。您是否正确定义了延迟加载?

您可以通过调用

直接加载这些引用

.Include()

所以您需要更改

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

然后您可以进行其余的过滤和投影。