首页 > 解决方案 > EF Core Include() 语句对于 IQueryable 为空

问题描述

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

本质上,我正在做一个涉及一对多关系的查询(目前在 ef core 2.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);
}

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

它需要是 IQueryable 的原因是可以应用分页,从而减少从数据库中提取的信息量。

任何帮助表示赞赏。

标签: c#entity-frameworkentity-framework-coreef-core-2.1

解决方案


这个问题有两个部分:

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 认为即使您使用它也不需要它......它可能会在 VisualInclude()的窗口上显示警告OutputStudio),否则它会尝试仅投影和请求它从new表达式中理解的字段(不包括Users)。

所以你需要在这里明确......尝试:

ActiveUsers = Users.Count(u => !u.IsDeleted && u.Enabled && u.UserType == UserType.Active);

并将Users实际包括在内。

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

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

EF Core 允许这样做(EF6 没有),但您可以将其配置为在发生这种情况时抛出错误(在内存中评估的查询):

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        /* etc. */
        .ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}

您可以在此处阅读有关此内容的更多信息:https ://docs.microsoft.com/en-us/ef/core/querying/client-eval


推荐阅读