[英]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.