[英]Entity Framework related objects using generics
在對實體使用Linq時,有關將Include()和Load()用於相關表信息存在很多問題。 我對這個問題有不同的看法。
我的情況:
我有一個表,該表為系統中的每個用戶保存許多時間記錄,並且我使用存儲庫模式和泛型進行開發,因此我的所有實體都有一個接口,可用於標准方法調用。 我已關閉了延遲加載,並且自己加載了所有數據。 因此,存儲庫中用於從具有相關表的表中加載所有記錄的代碼如下:
public class Repository<T> : IRepository<T> where T : class
{
protected readonly ApplicationDbContext Context;
public Repository(IConnectionHelper connection)
{
Context = connection.Context;
}
public virtual DbSet<T> ObjectSet
{
get { return Context.Set<T>(); }
}
public List<T> GetAll(String[] include, Expression<Func<T, bool>> predicate)
{
DbQuery<T> outQuery = null;
foreach (String s in include)
{
outQuery = ObjectSet.Include(s);
outQuery.Load();
}
return outQuery.Where(predicate).ToList();
}
}
該方法的調用如下:
string[] includes = { "User.UserProfile", "CampaignTimeClocks.CampaignRole.Campaign", "Site", "Type" };
DateTime uTcCurrent = GetUtc();
DateTime MinClockinDate = uTcCurrent.AddHours(-10);
List<TimeClock> tcPending = _timeClock.GetAll(includes, x => (x.PendingReview || x.ClockInDate < MinClockinDate && x.ClockOutDate == null) && (x.Site.Id == currentUser.SiteId));
當此方法運行並加載第一個User.Profile表時,它將加載所有時間記錄並將其與所有用戶相關聯,這需要一分鍾以上的時間,這太長了,因為最終記錄數僅185條記錄,但查詢的初始負載正在運行27,000 * 560用戶,即1500萬條記錄,並且隨着時間的推移,這種情況只會變得更糟。
問題是如何在沒有這種負載開銷的情況下如何做到這一點,我知道我可以鏈接包含,但是由於包含的數量將根據所調用的數據的類型和用途而改變,因此我不能簡單地對a進行硬編碼包含鏈。
我也嘗試過:
List<TimeClock> testLst = _timeClock.GetAll(x => x.PendingReview ||
(x.ClockInDate < MinClockinDate && x.ClockOutDate == null))
.Select(x => new TimeClock{Id = x.Id,
ClockInDate = x.ClockInDate,
ClockOutDate = x.ClockOutDate,
TotalClockTime = x.TotalClockTime,
Notes = x.Notes,
PendingReview = x.PendingReview,
Type = x.Type,
User = x.User,
CampaignTimeClocks = x.CampaignTimeClocks,
TimeClockAdjustments = x.TimeClockAdjustments,
Site = x.User.Site}).ToList();
這將為我提供User.Profile信息,但Site和Type屬性為null。
因此,我對如何在此處加載所需的數據感到迷茫。
非常感謝所有幫助。
你能先得到初始清單嗎
List<TimeClock> testLst = _timeClock.Where(x => x.PendingReview || (x.ClockInDate < MinClockinDate && x.ClockOutDate == null)).ToList();
然后調用以T作為參數的修改后的GetAll()
?
您所做的每個include最終都會在db中執行聯接。 假設您的左表的記錄大小非常大,為1024個字節,並且您有許多詳細信息,例如1000,並且詳細記錄的大小僅為100。這將導致左表的信息重復1000次,該信息db將被連接起來,EF必須過濾掉重復的副本以創建您的左實例。
最好不使用include並進行顯式加載。 基本上在相同的上下文中執行2個查詢。
我有一個這樣的例子,與您的例子不同,但我希望您能理解。 它可能比依賴include快多達10倍。 (數據庫只能有效地處理有限數量的聯接)
var adressen = adresRepository
.Query(r => r.RelatieId == relatieId)
.Include(i => i.AdresType)
.Select().ToList();
var adresids = (from a in adressen select a.AdresId).ToList();
IRepositoryAsync<Comm> commRepository = unitOfWork.RepositoryAsync<Comm>();
var comms = commRepository
.Query(c => adresids.Contains(c.AdresId))
.Include(i => i.CommType)
.Select();
對於我使用include的commType和adresType,因為存在1對1的關系,我避免了太多的聯接,因此我的多個查詢將比使用include的單個查詢更快。 我沒有在第一個查詢中包括Comms來嘗試避免第二個查詢,關鍵是在這種情況下,兩個查詢比單個查詢要快。
請注意,我的代碼是使用我自己的存儲庫構建的,因此該代碼對您不起作用,但是您可以從中得到啟發。
我發現不使用Load()語句即可更有效地執行此操作的方法是將DBQuery更改為IQueryable並鏈接包含,返回執行的查詢結果,以及一起刪除DBQuery.Load()。 這將查詢的執行時間從秒更改為毫秒。
public List<T> GetAll(String[] include)
{
IQueryable<T> outQuery = ObjectSet;
foreach (String s in include)
{
outQuery = outQuery.Include(s);
}
return outQuery.ToList();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.