cra*_*mmy 1 .net c# linq entity-framework entity-framework-core
我正在运行这样的代码:
var somethings = db.Somethings.Select(s => new SomethingViewModel
{
Id = s.Id,
Name = s.Name,
IsActive = s.IsActive
SubSomethings = s.SubSomethings.Select(ss => new SubSomethingViewModel
{
Id = ss.Id,
Name = ss.Name,
IsActive = ss.IsActive
}).Where(wss => wss.IsActive)
}).Where(ws => ws.IsActive && (ws.SubSomethings.Any())) //remove elements if no SubSomethings
.ToList();
Run Code Online (Sandbox Code Playgroud)
如您所见,这是一对多的关系.Something中有一个SubSomethings列表.如果我取出&&(ws.SubSomethings.Any()),我会得到一个非常快的列表.
但是,我想只在列表中包含至少有一个SubSomething的Somethings.我也尝试了以下并获得了同样可怕的效率:
var somethings = db.Somethings.Select(s => new SomethingViewModel
{
Id = s.Id,
Name = s.Name,
IsActive = s.IsActive
SubSomethings = s.SubSomethings.Select(ss => new SubSomethingViewModel
{
Id = ss.Id,
Name = ss.Name,
IsActive = ss.IsActive
}).Where(wss => wss.IsActive)
}).Where(ws => ws.IsActive)
.ToList(); //this finishes very quickly
var somethings2 = somethings.Where(s => s.SubSomethings.Any()).ToList(); //This is where the code bogged down
Run Code Online (Sandbox Code Playgroud)
如何重新编写查询以使代码更快更快?有一点需要注意:这适用于一两条记录.当我达到> 8000条记录时,至少需要四分钟.
这是我在SubSomething表上为SomethingId的外键创建的索引,它对应于Something.Id
CREATE NONCLUSTERED INDEX [IX_SubSomething_SomethingId] ON [dbo].[SubSomething]
(
[SomethingId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO
Run Code Online (Sandbox Code Playgroud)
这是SubSomething.SomethingId的外键创建:
ALTER TABLE [dbo].[SubSomething] WITH CHECK ADD CONSTRAINT [FK_SubSomething_Something_SomethingId] FOREIGN KEY([SomethingId])
REFERENCES [dbo].[Something] ([Id])
GO
ALTER TABLE [dbo].[SubSomething] CHECK CONSTRAINT [FK_SubSomething_Something_SomethingId]
GO
Run Code Online (Sandbox Code Playgroud)
EF Core是你的问题.目前,当查询包含子集合投影时,已知执行N + 1子查询.
解决它并将整个事情限制为2个SQL查询的唯一方法是尽可能多地过滤,然后使用预先加载在内存中加载包含子实体的整个实体集,然后切换到LINQ to Objects并进行最终投影/过滤:
var somethings = db.Somethings
.Include(s => s.SubSomethings)
.Where(s => s.IsActive)
.AsEnumerable()
.Select(s => new SomethingViewModel
{
Id = s.Id,
Name = s.Name,
IsActive = s.IsActive,
SubSomethings = s.SubSomethings.Select(ss => new SubSomethingViewModel
{
Id = ss.Id,
Name = ss.Name,
IsActive = ss.IsActive
}).Where(wss => wss.IsActive).ToList()
})
.Where(s => s.SubSomethings.Any())
.ToList();
Run Code Online (Sandbox Code Playgroud)