EF Core Include() 语句对于 IQueryable 为 null
EF Core Include() statement is null for IQueryable
好的,如果没有大量代码支持,这可能有点难以解释,但我会尽力而为。
基本上我正在做一个涉及一对多关系的查询(目前在 ef core 2.1 上)。但是,"many" 集合在具体化时为空。
这是有问题的查询(为简洁起见删除了一些代码)
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);
}
但是,这将引发空引用异常,因为 Users 集合为空。
现在我的问题是,当我明确要求加载用户集合时,为什么它为空?
目前的解决方法是将查询的第一行更改为 _managedSupportRepository.GetAllIncluding(m => m.Users).AsEnumerable()
这很荒谬,因为它会返回所有记录(几千条),因此性能不存在。
它需要是 IQueryable 的原因是可以应用分页,从而减少从数据库中提取的信息量。
感谢任何帮助。
这个问题有两个部分:
1) 不包含在投影中不包含
当您在提供程序上对 EF 进行查询时(服务器评估),您不会执行您的 new
表达式,因此:
ActiveUsers = ms.GetConsumed(),
从未真正执行过 ms.GetConsumed()
。您为 new
传递的表达式被解析,然后 t运行 指定为查询(SQL 在 sql 服务器的情况下),但是 ms.GetConsumed()
是未在提供者上执行(在对数据库的查询上)。
因此您需要在 表达式 中包含 Users
。例如:
select new AccountViewModel
{
AccountName = a.AccountName,
AllUsers = Users.ToList(),
ActiveUsers = ms.GetConsumed(),
// etc.
}
这样 EF 就知道查询需要 Users
并实际包含它(您没有在表达式中使用 Users
,因此 EF 认为它不需要它,即使您 Include()
它...它可能会在 Visual Studio 中的 Output
window 上显示警告,否则它会尝试仅投影和请求它从 new
表达式(不包括 Users
)。
所以你需要在这里明确...尝试:
ActiveUsers = Users.Count(u => !u.IsDeleted && u.Enabled && u.UserType == UserType.Active);
而Users
将实际包含在内。
2) 当查询无法得到 t运行slated
时自动进行客户端评估
在这种情况下,Users
将被包括在内,因为它在实际表达式中...但是,EF 仍然不知道如何 t运行slate ms.GetConsumed()
到提供程序查询,所以它会工作(因为 Users
将被加载),但它不会在数据库上 运行,它仍然是运行 在内存上(它将进行客户端投影)。同样,如果您在 运行 那里 运行,您应该会在 Visual Studio 的 Output
window 中看到关于此的警告。
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
好的,如果没有大量代码支持,这可能有点难以解释,但我会尽力而为。
基本上我正在做一个涉及一对多关系的查询(目前在 ef core 2.1 上)。但是,"many" 集合在具体化时为空。
这是有问题的查询(为简洁起见删除了一些代码)
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);
}
但是,这将引发空引用异常,因为 Users 集合为空。
现在我的问题是,当我明确要求加载用户集合时,为什么它为空?
目前的解决方法是将查询的第一行更改为 _managedSupportRepository.GetAllIncluding(m => m.Users).AsEnumerable()
这很荒谬,因为它会返回所有记录(几千条),因此性能不存在。
它需要是 IQueryable 的原因是可以应用分页,从而减少从数据库中提取的信息量。
感谢任何帮助。
这个问题有两个部分:
1) 不包含在投影中不包含
当您在提供程序上对 EF 进行查询时(服务器评估),您不会执行您的 new
表达式,因此:
ActiveUsers = ms.GetConsumed(),
从未真正执行过 ms.GetConsumed()
。您为 new
传递的表达式被解析,然后 t运行 指定为查询(SQL 在 sql 服务器的情况下),但是 ms.GetConsumed()
是未在提供者上执行(在对数据库的查询上)。
因此您需要在 表达式 中包含 Users
。例如:
select new AccountViewModel
{
AccountName = a.AccountName,
AllUsers = Users.ToList(),
ActiveUsers = ms.GetConsumed(),
// etc.
}
这样 EF 就知道查询需要 Users
并实际包含它(您没有在表达式中使用 Users
,因此 EF 认为它不需要它,即使您 Include()
它...它可能会在 Visual Studio 中的 Output
window 上显示警告,否则它会尝试仅投影和请求它从 new
表达式(不包括 Users
)。
所以你需要在这里明确...尝试:
ActiveUsers = Users.Count(u => !u.IsDeleted && u.Enabled && u.UserType == UserType.Active);
而Users
将实际包含在内。
2) 当查询无法得到 t运行slated
时自动进行客户端评估在这种情况下,Users
将被包括在内,因为它在实际表达式中...但是,EF 仍然不知道如何 t运行slate ms.GetConsumed()
到提供程序查询,所以它会工作(因为 Users
将被加载),但它不会在数据库上 运行,它仍然是运行 在内存上(它将进行客户端投影)。同样,如果您在 运行 那里 运行,您应该会在 Visual Studio 的 Output
window 中看到关于此的警告。
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