对于IQueryable,EF Core Include()语句为null

时间:2018-07-25 12:16:13

标签: c# entity-framework entity-framework-core ef-core-2.1

好吧,所以如果没有大量的代码来支持它可能很难解释,但我会尽力而为。

本质上,我正在执行一个查询(涉及ef核心2.1),涉及1到许多关系。但是,“很多”集合在实现时为空。

这是有问题的查询(为简洁起见,删除了一些代码)

IQueryable<AccountViewModel> baseQuery = from ms in _managedSupportRepository.GetAllIncluding(m => m.Users) // here is the problem
                                          // a few lines of filters like the one below
                                          where string.IsNullOrEmpty(clientVersionFilter) || !string.IsNullOrEmpty(ms.ClientVersion) && ms.ClientVersion.Contains(clientVersionFilter, StringComparison.OrdinalIgnoreCase)
                                          join c in _contractRepository.GetAll() on ms.Id equals c.AssetId into contracts
                                          from c in contracts.DefaultIfEmpty()
                                          let isAssigned = c != null
                                          where !isAssignedFilter.valueExists || isAssignedFilter.value == isAssigned
                                          join a in _autotaskAccountRepository.GetAll() on ms.TenantId equals a.Id
                                          where string.IsNullOrEmpty(accountNameFilter) || !string.IsNullOrEmpty(a.AccountName) && a.AccountName.Contains(accountNameFilter, StringComparison.OrdinalIgnoreCase)
                                          select new AccountViewModel
                                          {
                                              AccountName = a.AccountName,
                                              ActiveUsers = ms.GetConsumed(), // here is the problem
                                              ClientVersion = ms.ClientVersion,
                                              ExternalIpAddress = ms.IpAddress,
                                              Hostname = ms.Hostname,
                                              Id = ms.Id,
                                              IsActive = ms.IsActive,
                                              IsAssigned = isAssigned,
                                              LastSeen = ms.CheckInTime,
                                              Status = ms.Status
                                          };

int count = baseQuery.Count();

baseQuery = baseQuery.Paging(sortOrder, start, length);

return (baseQuery.ToList(), count);

为清楚起见,_managedSupportRepository.GetAllIncluding(m => m.Users)方法只是.Include()方法的包装。

因此问题出在活动用户ActiveUsers = ms.GetConsumed(),的视图模型中。 GetConsumed()方法如下

public long GetConsumed()
{
    return Users.Count(u => !u.IsDeleted && u.Enabled && u.UserType == UserType.Active);
}

但是,这将引发null引用异常,因为Users集合为null。 现在我的问题是,当我明确要求加载时,为什么Users集合为null? 目前的一种解决方法是将查询的第一行更改为_managedSupportRepository.GetAllIncluding(m => m.Users).AsEnumerable(),这很荒谬,因为它将所有记录都带回来(几千条),因此性能不存在。

它必须为IQueryable的原因是可以应用分页,从而减少了从数据库提取的信息量。

感谢您的帮助。

1 个答案:

答案 0 :(得分:3)

此问题有两个部分:

1)投影中未包含的内容不会被包含

当您在提供程序上对EF进行查询(服务器评估)时,您不是在执行new表达式,因此:

ActiveUsers = ms.GetConsumed(),

从不实际执行ms.GetConsumed()。您为new传递的表达式将被解析,然后转换为查询(如果是sql server,则为SQL),但是ms.GetConsumed()不会在提供程序上执行(对数据库的查询)。

因此,您需要在表达式中包含Users。例如:

select new AccountViewModel
{
    AccountName = a.AccountName,
    AllUsers = Users.ToList(),
    ActiveUsers = ms.GetConsumed(),
    // etc.
}

通过这种方式,EF知道查询需要Users并实际上包含了该查询(您在表达式中未使用Users,因此EF认为即使您{{1 }} ...它可能会在Visual Studio的Include()窗口中显示警告,否则它将尝试投影并仅请求从Output表达式中理解的字段(它不会t包括new)。

因此,您需要在此处明确...尝试:

Users

实际上将包含ActiveUsers = Users.Count(u => !u.IsDeleted && u.Enabled && u.UserType == UserType.Active);

2)无法翻译查询时自动进行客户端评估

在这种情况下,将包含Users,因为它在实际的表达式中... 但是,EF仍然不知道如何将Users转换为提供者查询,因此它可以工作(因为ms.GetConsumed()将被加载),但是它不会在数据库上运行,它仍将在内存上运行(它将进行客户端投影)。同样,如果您在Visual Studio的Users窗口中运行此警告,则应该对此进行警告。

EF Core允许这样做(EF6不允许),但是您可以将其配置为在发生这种情况时引发错误(查询将在内存中进行评估):

Output

您可以在此处了解更多信息:https://docs.microsoft.com/en-us/ef/core/querying/client-eval