简体   繁体   English

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

[英]EF Core Include() statement is null for IQueryable

Ok, so this might be a bit hard to explain without a ton of code to support it but I will try my best. 好的,因此如果没有大量代码来支持它可能会有点难以解释,但我会尽力而为。

Essentially I am doing a query (currently on ef core 2.1) with involves a 1 to many relationships. 本质上,我正在执行一个涉及一对多关系的查询(当前在ef核心2.1上)。 However, the "many" collection is null when it materialized. 但是,“许多”集合在实现时为空。

Here is the query in question (some code removed for brevity) 这是有问题的查询(为简洁起见删除了一些代码)

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);

Just for clarity, the _managedSupportRepository.GetAllIncluding(m => m.Users) method is just a wrapper around the .Include() method. 为了清楚起见, _managedSupportRepository.GetAllIncluding(m => m.Users)方法只是.Include()方法的包装。

So the problem is in the view model for active users ActiveUsers = ms.GetConsumed(), . 因此,问题在于活动用户的视图模型ActiveUsers = ms.GetConsumed(), The GetConsumed() method is as follows GetConsumed()方法如下

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

however, this throws a null reference exception because the Users collection is null. 但是,这将引发null引用异常,因为Users集合为null。 Now my question is, why is the Users collection null when I am explicitly asking it to be loaded? 现在我的问题是,当我明确要求加载时,为什么Users集合为null? A workaround at the moment is to alter the queries first line to be this _managedSupportRepository.GetAllIncluding(m => m.Users).AsEnumerable() which is just ridiculous as it brings all the records back (which is several thousand) so performance is nonexistent. 目前的一种解决方法是将查询的第一行更改为_managedSupportRepository.GetAllIncluding(m => m.Users).AsEnumerable() ,这很可笑,因为它将所有记录都带回了(几千条),因此性能是不存在的。

The reason it needs to be an IQueryable is so the paging can be applied, thus reducing the amount of information pulled from the database. 它必须是IQueryable的原因是可以应用分页,从而减少了从数据库中提取的信息量。

Any help is appreciated. 任何帮助表示赞赏。

There are two parts to this problem: 此问题有两个部分:

1) Includes not in the projection don't get included 1)不包含在投影中不包含在内

When you do queries on EF on the provider (server evaluation), you are not -executing- your new expressions, so this: 当您在提供程序上对EF进行查询(服务器评估)时,您不会“执行” new表达式,因此:

ActiveUsers = ms.GetConsumed(),

Never actually executes ms.GetConsumed() . 从不实际执行ms.GetConsumed() The expression you pass in for the new is parsed and then translated to the query (SQL in case of sql server), but ms.GetConsumed() is not executed on the provider (on the query to the database). 您将为new传递的表达式进行解析,然后转换为查询(对于sql server,为SQL),但是不会在提供程序上(对数据库的查询ms.GetConsumed()执行ms.GetConsumed() )。

So you need to include Users on the expression . 因此,您需要在表达式上包括Users For example: 例如:

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

This way EF knows it needs Users for the query and actually includes it (you are not using Users in your expression, so EF thinks it doesn't need it even if you Include() it... it'll probably show a warning on the Output window in Visual Studio), otherwise it tries to project and request only the fields it understands from the new expression (which doesn't include Users ). 这样,EF知道它需要Users查询并实际包含它(您没有在表达式中使用Users ,因此EF认为即使您Include()它也不需要它...它可能会显示警告(在Visual Studio的“ Output窗口中),否则它将尝试投影并仅请求new表达式(不包括Users )中理解的字段。

So you need to be explicit here... try: 因此,您需要在此处明确...尝试:

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

And Users will be actually included. 并且实际上将包括Users

2) Automatic client side evaluation when queries can't get translated 2)无法翻译查询时自动进行客户端评估

In this case, Users will be included because it's in the actual expression... BUT , EF still doesn't know how to translate ms.GetConsumed() to the provider query, so it'll work (because Users will be loaded), but it won't be ran on the database, it'll still be run on memory (it'll will do client side projection). 在这种情况下,将包含Users因为它在实际的表达式中... 但是 ,EF仍然不知道如何将ms.GetConsumed()转换为提供程序查询,因此它将起作用(因为将加载Users ) ,但不会在数据库上运行,它仍将在内存上运行(它将进行客户端投影)。 Again, you should see a warning about this in the Output window in Visual Studio if you are running it there. 同样,如果在Visual Studio的“ Output窗口中运行它,则应该在此看到警告。

EF Core allows this (EF6 didn't), but you can configure it to throw errors if this happens (queries that get evaluated in memory): EF Core允许这样做(EF6不允许),但是您可以将其配置为在发生这种情况时引发错误(在内存中评估查询):

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

You can read more about this here: https://docs.microsoft.com/en-us/ef/core/querying/client-eval 您可以在此处阅读有关此内容的更多信息: https : //docs.microsoft.com/zh-cn/ef/core/querying/client-eval

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM