我有以下方法從導航屬性加載相關資料。但是,它會生成錯誤。我可以通過添加ToList()or來消除錯誤ToArray(),但出于性能原因我寧愿不這樣做。我也無法MARS在我的 web.config 檔案中設定該屬性,因為它會導致其他連接類出現問題。
如何在不使用擴展方法或編輯我的web.config?
public override void Load(IEnumerable<Ques> data)
{
if (data.Any())
{
foreach (Ques qst in data)
{
if (qst?.Id_user != null)
{
db.Entry(qst).Reference(q => q.AspNetUsers).Load();
}
}
}
}
uj5u.com熱心網友回復:
我從這個問題中得出你有這樣的情況:
// (outside code)
var query = db.SomeEntity.Wnere(x => x.SomeCondition == someCondition);
LoadDependent(query);
機會基于此方法,它可能是構建搜索運算式等的各種方法的呼叫堆疊,但最終傳入的LoadDependent()是IQueryable<TEntity>.
相反,如果你打電話:
// (outside code)
var query = db.SomeEntity.Wnere(x => x.SomeCondition == someCondition);
var data = query.ToList();
LoadDependent(data);
或者..在你的 LoadDependent 改變做這樣的事情:
base.LoadDependent(data);
data = data.ToList();
或更好,
foreach (Ques qst in data.ToList())
然后您的LoadDependent()呼叫有效,但在第一個示例中,您收到 DataReader 已打開的錯誤。這是因為您foreach按原樣呼叫將迭代IQueryableEF 的資料讀取器將保持打開的含義,因此無法進一步呼叫db,我假設它是注入的 DbContext 的模塊級變數。
替換這個:
db.Entry(qst).Reference(q => q.AspNetUsers).Load();
有了這個:
db.Entry(qst).Reference(q => q.AspNetUsers).LoadAsync();
...實際上不起作用。這只是異步委托加載呼叫,如果不等待它,它也會失敗,只是不會在延續執行緒上引發例外。
正如對您的問題的評論中所述,這是處理加載參考的一個非常糟糕的設計選擇。如果您不打算使用預先加載或投影正確實作初始獲取,那么啟用延遲加載并在實際需要參考時選擇 n 1 命中要好得多。
像這樣的代碼在整個代碼中強制使用 Select n 1 模式。
加載“Ques”的一個很好的例子,它與用戶預加載相關聯:
var ques = db.Ques
.Include(x => x.AspNetUsers)
.Where(x => x.SomeCondition == someCondition)
.ToList();
無論“SomeCondition”導致回傳 1 個查詢還是回傳 1000 個查詢,資料都將執行一次對資料庫的查詢。
Select n 1 方案是不好的,因為在呼叫獲取依賴項回傳 1000 個 Ques 的情況下,您會得到:
var ques = db.Ques
.Where(x => x.SomeCondition == someCondition)
.ToList(); // 1 query.
foreach(var q in ques)
db.Entry(q).Reference(x => x.AspNetUsers).Load(); // 1 query x 1000
運行 1001 個查詢。這與您要加載的每個參考相結合。
Which then looks problematic where later code might want to offer pagination such as to take only 25 items where the total record count could run in the 10's of thousands or more. This is where lazy loading would be the lesser of two Select n 1 evils, as with lazy loading you know that AspNetUsers would only be selected if any returned Ques actually referenced it, and only for those Ques that actually reference it. So if the pagination only "touched" 25 rows, Lazy Loading would result in 26 queries. Lazy loading is a trap however as later code changes could inadvertently lead to performance issues appearing in seemingly unrelated areas as new referenences or code changes result in far more references being "touched" and kicking off a query.
If you are going to pursue a LoadDependent() type method then you need to ensure that it is called as late as possible, once you have a known set size to load because you will need to materialize the collection to load related entities with the same DbContext instance. (I.e. after pagination) Trying to work around it using detached instances (AsNoTracking()) or by using a completely new DbContext instance may give you some headway but will invariably lead to more problems later, as you will have a mix of tracked an untracked entities, or worse, entities tracked by different DbContexts depending on how these loaded entities are consumed.
An alternative teams pursue is rather than a LoadReference() type method would be an IncludeReference() type method. The goal here being to build .Include statements into the IQueryable. This can be done two ways, either by magic strings (property names) or by passing in expressions for the references to include. Again this can turn into a bit of a rabbit hole when handling more deeply nested references. (I.e. building .Include().ThenInclude() chains.) This avoids the Select n 1 issue by eager loading the required related data.
uj5u.com熱心網友回復:
我已經通過洗掉方法解決了這個問題,LoadDepend并且我Include()在我的第一次資料查詢中使用了在導航屬性中顯示參考資料
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/335918.html
