gpt4 book ai didi

c# - LINQ包括搜索时降低性能

转载 作者:行者123 更新时间:2023-11-30 19:35:53 27 4
gpt4 key购买 nike

我们具有以下方法,使我们可以在Projects表中搜索DataGrid:

public async Task<IEnumerable<Project>> GetFilteredProjects(string searchString)
{
var projects = _context.Projects.Where(p => p.Current);

projects.Include(p => p.Client);
projects.Include(p => p.Architect);
projects.Include(p => p.ProjectManager);

if (!string.IsNullOrEmpty(searchString))
{
projects = projects
.Where(p => p.NormalizedFullProjectName.Contains(searchString)
|| p.Client.NormalizedName.Contains(searchString)
|| p.Architect.NormalizedFullName.Contains(searchString)
|| p.ProjectManager.NormalizedFullName.Contains(searchString));
}

projects = projects.OrderBy(p => p.Name).Take(10);

return await projects.ToListAsync();
}


如果我们不在项目上使用 Include,则搜索是即时的。但是,在搜索中添加它们后可能需要3秒钟以上。

我们需要包括其他实体,以允许用户根据需要进行搜索。

我们如何提高性能,但仍然保留 Include以便对其进行搜索?

没有 Incldue的方法如下所示:

public async Task<IEnumerable<Project>> GetFilteredProjects(string searchString)
{
var projects = _context.Projects.Where(p => p.Current);

if (!string.IsNullOrEmpty(searchString))
{
projects = projects
.Where(p => p.Name.Contains(searchString));
}

projects = projects.OrderBy(p => p.Name).Take(10);

return await projects.ToListAsync();
}


没有 Include的情况如下所示:

enter image description here

使用 Include

enter image description here

最佳答案

简短的答案是,包括所有额外的实体需要时间和精力,因此会增加加载时间。

但是,您的假设存在一个缺陷:


  我们需要包括其他实体,以允许用户根据需要进行搜索。


那是(不一定)正确的。过滤发生在数据库级别。 Include告诉实体框架从数据库中加载记录。这是两件事。

看下面的例子:

_context.Projects
.Include(p => p.Architect)
.Where(p => p.Architect.Name == "Bob")
.ToList()


这将为您提供一个具有名为Bob的建筑师的项目(及其建筑师)列表。

_context.Projects
.Where(p => p.Architect.Name == "Bob")
.ToList()


这将为您提供一个项目列表(没有建筑师),其中有一个名为Bob的建筑师。但实际上并不会将 Architect对象加载到内存中。

_context.Projects
.Include(p => p.Architect)
.ToList()


这将为您提供项目列表(及其建筑师)。它将包含每个项目,列表未过滤。



您只需要在要进行内存中过滤时,即在已经从数据库加载的集合上使用 Include

是否适合您取决于这部分:

    projects = projects
.Where(p => p.NormalizedFullProjectName.Contains(searchString)
|| p.Client.NormalizedName.Contains(searchString)
|| p.Architect.NormalizedFullName.Contains(searchString)
|| p.ProjectManager.NormalizedFullName.Contains(searchString));


如果 NormalizedFullProjectName(和其他属性)是数据库列,则EF能够在数据库级别执行过滤。您不需要 Include来过滤项目。

如果 NormalizedFullProjectName(和其他属性)不是数据库列,则EF在应用过滤器之前,首先必须将其加载到内存中。在这种情况下,您确实需要 Include,因为需要将架构师(和其他设计师)加载到内存中。



如果您仅出于过滤目的(而不是出于显示目的)加载相关实体,并且您正在数据库级别进行过滤;那么您只需删除include语句。

如果需要加载那些相关的实体(用于内存过滤或出于显示目的),那么除非编写了指定所需字段的 Include,否则就很难删除 Select语句。

例如:

_context.Projects
.Select(p => new { Project = p, ArchitectName = p.Architect.Name })
.ToList()


这将加载项目实体(全部),但仅加载架构师的名称,而不加载其他属性。如果您的相关实体具有许多当前不需要的属性,则可以大大提高性能。

注意:当前示例使用匿名类型。我通常主张为此创建一个自定义类型。但这与我们在此解决的性能问题无关。



更新资料

根据您的更新,您似乎暗示预期的过滤是在从数据库加载对象之后进行的。

这是性能问题的根源。您正在获取大量数据,但仅显示其中一部分。未显示的数据仍然需要加载,这是浪费时间。

这里有单独的性能参数:


一次加载所有内容-一次加载所有数据(这可能需要很长时间),但随后允许用户筛选已加载的数据(速度非常快)
加载块-仅加载与应用的过滤器匹配的数据。如果用户更改过滤器,则会再次加载数据。第一次加载将更快,但是与内存中筛选相比,后续的筛选操作将花费更长的时间。


您在这里应该做什么不是我的决定。这是优先事项。一些客户比另一个更喜欢。我要说的是,在大多数情况下,第二个选项(加载块)在这里是更好的选择,因为如果用户从未浏览过其中的90%,它可以避免不必要地加载海量数据集。这浪费了性能和网络负载。

我给出的答案适用于“加载块”方法。

如果决定采用“一次加载所有内容”的方法,那么您将不得不接受该初始加载带来的性能损失。最好的办法是严格限制返回的数据列(如我在 Select中所示),以最大程度地降低性能/网络成本。

我认为没有合理的理由将这两种方法混合使用。您将面临两个弊端。

关于c# - LINQ包括搜索时降低性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50602430/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com