[英]Entity Framework Core Nested List Performance
當我嘗試向嵌套列表中添加其他項目時,我在實體框架核心性能方面遇到了困難。
舉個例子:我有多個項目,項目包含多個房屋,房屋有多個立面,立面有多個windows。 如果我現在想向特定項目、房屋、立面添加額外的 window,我會這樣做:
public async Task SaveWindowAsync(Guid projectId, Guid houseId, Guid facadeId, WindowEntity windowEntity)
{
using (ProjectsDbContext context = new ProjectsDbContext())
{
var windowList = context.ProjectSet
.Include(p => p.Houses)
.ThenInclude(h => h.Facades)
.ThenInclude(f => f.Windows)
.First(p => p.Id == projectId).Houses
.First(h => h.Id == houseId).Facades
.First(f => f.Id == facadeId).Windows;
windowList.Add(windowEntity);
await context.SaveChangesAsync();
}
}
這在功能方面效果很好。 但是,當數據庫增加時,性能會越來越慢。 是否有更高效的方式將項目添加到嵌套列表?
更新 1
我用這個包含 50 個項目的未來派對象創建了一個簡單的測試數據庫,每個項目有 10 個房子,每個房子有 10 個立面,每個立面有 10 個 windows。 這導致數據庫大小約為 10Mb。
在測試中,我一個接一個地添加了 1000 Windows(沒有散裝):
上面提到的解決方案需要145s的總時間。
@David Browne 提到的解決方案——微軟大約需要 54s
var facadeEntity = context.Set<FacadeEntity>()
.Include(f => f.Windows)
.Single(f => f.Id == facadeId);
facadeEntity.Windows.Add(windowEntity);
await context.SaveChangesAsync();
更新 2
正如@David Browne 推薦的那樣,我在 window 中添加了一個 ForeignKey:
modelBuilder.Entity<FacadeEntity>()
.HasMany(f => f.Windows).WithOne()
.HasForeignKey(f => f.FacadeId)
.OnDelete(DeleteBehavior.Cascade);
保存是這樣執行的:
context.Entry(windowEntity).Property(nameof(WindowEntity.FacadeId)).CurrentValue = facadeId;
context.Set<WindowEntity>().Add(windowEntity);
await context.SaveChangesAsync();
這個問題是一樣的 windows 我有更長的添加時間。 1000 個 Windows 的持續時間約為 53 秒。
我目前只有一個“DbSet ProjectSet”,你會在上下文中添加一個額外的 DbSet 嗎?
如果您沒有為實體聲明DbSet<T>
,請通過DbContext.Set<T>()
訪問它,例如:
public static async Task SaveWindowAsync(Guid projectId, Guid houseId, Guid facadeId, Window windowEntity)
{
using (ProjectsDbContext context = new ProjectsDbContext())
{
var facade = context.Set<Facade>()
.Where(f => f.FacadeId == facadeId)
.Single();
facade.Windows.Add(windowEntity);
await context.SaveChangesAsync();
}
}
這轉化為:
SELECT TOP(2) [f].[FacadeId], [f].[HouseId]
FROM [Facade] AS [f]
WHERE [f].[FacadeId] = @__facadeId_0
接着:
INSERT INTO [Window] ([WindowId], [FacadeId])
VALUES (@p0, @p1)
假設Facade
有一個單列主鍵。 如果它具有 (ProjectId,HouseId,FacadeId) 的復合鍵,則將它們添加到Where
。
但是,最好的方法是設置 Window.FacadeId 的外鍵屬性,並且根本不加載外觀。 在 EF Core 中,如果您沒有外鍵屬性,則可以使用影子屬性執行此操作。 例如:
public static async Task SaveWindowAsync(Guid projectId, Guid houseId, Guid facadeId, Window windowEntity)
{
using (ProjectsDbContext context = new ProjectsDbContext())
{
context.Entry(windowEntity).Property("FacadeId").CurrentValue = facadeId;
context.Set<Window>().Add(windowEntity);
await context.SaveChangesAsync();
}
}
如果您嘗試將Window 添加到特定項目/房屋/立面,您可以直接在 WindowEntity 上設置 FacadeId 並保存嗎? 據推測,Window 有一個 FacadeId 屬性,就像 Facade 有一個 HouseId,而 House 有一個 ProjectId。 如果 Window 有其所有父母的 Id(不必要),那么也只需設置 House 和 Project Id。
public async Task SaveWindowAsync(Guid projectId, Guid houseId, Guid facadeId, WindowEntity windowEntity)
{
using (ProjectsDbContext context = new ProjectsDbContext())
{
windowEntity.FacadeId = facadeId;
context.WindowSet.Add(windowEntity);
await context.SaveChangesAsync();
}
}
更新:如果您不想直接在實體上設置 facadeId,那么您可以只加載外觀,並設置其 Facade 屬性而不是 Id。 如果您要繼續使用該集合的實例,您也可以選擇將 windowEntity 添加到 Facade 的 Windows 集合中。
public async Task SaveWindowAsync(Guid facadeId, WindowEntity windowEntity)
{
using (ProjectsDbContext context = new ProjectsDbContext())
{
var facade = Facades.Single(x => x.Id == facadeId);
windowEntity.Facade = facade;
facade.Windows.Add(windowEntity);
await context.SaveChangesAsync();
}
}
您應該首先從 Windows DbSet 中的 select 然后加入 Facades 然后加入房屋您的查詢應該看起來像這樣
var windowList = context.DbSet<Windows>().Where(w => w.Facade.Id== facadeId && w.Facade.House.Id == houseId && w.Facade.House.Project.Id == projectId)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.