[英]EF Core Include() statement is null for IQueryable
好的,因此如果沒有大量代碼來支持它可能會有點難以解釋,但我會盡力而為。
本質上,我正在執行一個涉及一對多關系的查詢(當前在ef核心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);
}
但是,這將引發null引用異常,因為Users集合為null。 現在我的問題是,當我明確要求加載時,為什么Users集合為null? 目前的一種解決方法是將查詢的第一行更改為_managedSupportRepository.GetAllIncluding(m => m.Users).AsEnumerable()
,這很可笑,因為它將所有記錄都帶回了(幾千條),因此性能是不存在的。
它必須是IQueryable的原因是可以應用分頁,從而減少了從數據庫中提取的信息量。
任何幫助表示贊賞。
此問題有兩個部分:
當您在提供程序上對EF進行查詢(服務器評估)時,您不會“執行” new
表達式,因此:
ActiveUsers = ms.GetConsumed(),
從不實際執行ms.GetConsumed()
。 您將為new
傳遞的表達式進行解析,然后轉換為查詢(對於sql server,為SQL),但是不會在提供程序上(對數據庫的查詢ms.GetConsumed()
執行ms.GetConsumed()
)。
因此,您需要在表達式上包括Users
。 例如:
select new AccountViewModel
{
AccountName = a.AccountName,
AllUsers = Users.ToList(),
ActiveUsers = ms.GetConsumed(),
// etc.
}
這樣,EF知道它需要Users
查詢並實際包含它(您沒有在表達式中使用Users
,因此EF認為即使您Include()
它也不需要它...它可能會顯示警告(在Visual Studio的“ Output
窗口中),否則它將嘗試投影並僅請求new
表達式(不包括Users
)中理解的字段。
因此,您需要在此處明確...嘗試:
ActiveUsers = Users.Count(u => !u.IsDeleted && u.Enabled && u.UserType == UserType.Active);
並且實際上將包括Users
。
在這種情況下,將包含Users
因為它在實際的表達式中... 但是 ,EF仍然不知道如何將ms.GetConsumed()
轉換為提供程序查詢,因此它將起作用(因為將加載Users
) ,但不會在數據庫上運行,它仍將在內存上運行(它將進行客戶端投影)。 同樣,如果在Visual Studio的“ Output
窗口中運行它,則應該在此看到警告。
EF Core允許這樣做(EF6不允許),但是您可以將其配置為在發生這種情況時引發錯誤(在內存中評估查詢):
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
/* etc. */
.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}
您可以在此處閱讀有關此內容的更多信息: https : //docs.microsoft.com/zh-cn/ef/core/querying/client-eval
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.