【发布时间】:2019-11-19 12:58:45
【问题描述】:
当我尝试向嵌套列表中添加其他项目时,我在实体框架的核心性能方面遇到了困难。
我们举个例子: 我有多个项目,项目包含多个房屋,房屋有多个立面,立面有多个窗户。 如果我现在想为特定项目、房屋、立面添加一个额外的窗口,我会这样做:
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 个窗户。这导致数据库大小约为 10Mb。
在测试中,我一个接一个地添加了 1000 个 Windows(没有批量):
上面提到的解决方案总共需要145s。
@David Browne 提到的解决方案——微软大约需要 54 秒
var facadeEntity = context.Set<FacadeEntity>()
.Include(f => f.Windows)
.Single(f => f.Id == facadeId);
facadeEntity.Windows.Add(windowEntity);
await context.SaveChangesAsync();
更新 2
按照@David Browne 的建议,我在窗口中添加了一个 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();
这个问题是一样的,我拥有的窗口越多,添加所需的时间就越长。 1000 个窗口的持续时间约为 53 秒。
【问题讨论】:
-
你能检查一下查询是如何形成的吗?除了
Include,您可以编写基于Join的查询来获取外观并将窗口添加到子对象中。 -
第一个
First将撤回第一个项目以及它的所有房屋、外墙和窗户。而是使用Where和SelectMany的组合进行过滤,然后取第一个。 -
@juharr 你的意思是: var windowList = context.ProjectSet.Where(p => p.Id == projectId).SelectMany(p => p.Houses).Where(h => h .Id == facadeId).SelectMany(f => f.Facades).Where(f => f.Id == facadeId).SelectMany(w => w.Windows).ToList(); .Where(f => f.Id == facadeId).SelectMany(w => w.Windows).ToList();
标签: c# .net database entity-framework