C# 低效的实体框架查询
我有以下声明:C# 低效的实体框架查询,c#,linq,entity-framework,C#,Linq,Entity Framework,我有以下声明: foreach (var articleId in cleanArticlesIds) { var countArt = context.TrackingInformations.Where(x => x.ArticleId == articleId).Count(); articleDictionary.Add(articleId, countArt); } 数据库看起来像这样 TrackingInformation(Id, ArticleId --so
foreach (var articleId in cleanArticlesIds)
{
var countArt = context.TrackingInformations.Where(x => x.ArticleId == articleId).Count();
articleDictionary.Add(articleId, countArt);
}
数据库看起来像这样
TrackingInformation(Id, ArticleId --some stuff
Article(Id, --some stuff
我想做的是从TrackingInformations表中获取所有文章ID计数。
例如:
ArticleId:1 Count:1
ArticleId:2 Count:8
ArticleId:3 Count:5
ArticleId:4 Count:0
所以我可以有一本字典
Context是实体框架DbContext。问题是此解决方案的运行速度非常慢,数据库中的文章数量超过了10k篇,它们应该会快速增长尝试下一次查询以收集分组数据,并添加缺少的信息。你们可以试着跳过Select子句,我不知道EF是否能很好地处理TODICTIONAL 如果遇到大量数据库请求的问题,可以在Select和ToDictionary之间添加ToList步骤,以便将所有必需的信息都带到内存中 这取决于您的所有映射配置、环境,因此为了获得良好的性能,您需要稍微处理不同的查询。主要的方法是在数据库级别聚合尽可能多的数据,只需很少的查询
var articleDictionary =
context.TrackingInformations.Where(trackInfo => cleanArticlesIds.Contains(trackInfo.ArticleId))
.GroupBy(trackInfo => trackInfo.ArticleId)
.Select(grp => new{grp.Key, Count = grp.Count()})
.ToDictionary(info => "ArticleId:" + info.Key,
info => info.Count);
foreach (var missingArticleId in cleanArticlesIds)
{
if(!articleDictionary.ContainsKey(missingArticleId))
articleDictionary.add(missingArticleId, 0);
}
尝试下一次查询以收集分组数据,并添加缺少的信息。你们可以试着跳过Select子句,我不知道EF是否能很好地处理TODICTIONAL 如果遇到大量数据库请求的问题,可以在Select和ToDictionary之间添加ToList步骤,以便将所有必需的信息都带到内存中 这取决于您的所有映射配置、环境,因此为了获得良好的性能,您需要稍微处理不同的查询。主要的方法是在数据库级别聚合尽可能多的数据,只需很少的查询
var articleDictionary =
context.TrackingInformations.Where(trackInfo => cleanArticlesIds.Contains(trackInfo.ArticleId))
.GroupBy(trackInfo => trackInfo.ArticleId)
.Select(grp => new{grp.Key, Count = grp.Count()})
.ToDictionary(info => "ArticleId:" + info.Key,
info => info.Count);
foreach (var missingArticleId in cleanArticlesIds)
{
if(!articleDictionary.ContainsKey(missingArticleId))
articleDictionary.add(missingArticleId, 0);
}
如果TrackingInformation是文章的可导航属性,则可以执行以下操作:
var result=context.Article.Select(a=>new {a.id,Count=a.TrackingInformation.Count()});
将其放入词典也很简单:
var result=context.Article
.Select(a=>new {a.id,Count=a.TrackingInformation.Count()})
.ToDictionary(a=>a.id,a=>a.Count);
如果TrackingForation不是可导航属性,则可以执行以下操作:
var result=context.Article.GroupJoin(
context.TrackingInformation,
foo => foo.id,
bar => bar.id,
(x,y) => new { id = x.id, Count = y.Count() })
.ToDictionary(a=>a.id,a=>a.Count);
如果TrackingInformation是文章的可导航属性,则可以执行以下操作:
var result=context.Article.Select(a=>new {a.id,Count=a.TrackingInformation.Count()});
将其放入词典也很简单:
var result=context.Article
.Select(a=>new {a.id,Count=a.TrackingInformation.Count()})
.ToDictionary(a=>a.id,a=>a.Count);
如果TrackingForation不是可导航属性,则可以执行以下操作:
var result=context.Article.GroupJoin(
context.TrackingInformation,
foo => foo.id,
bar => bar.id,
(x,y) => new { id = x.id, Count = y.Count() })
.ToDictionary(a=>a.id,a=>a.Count);
你对“慢”的定义是什么?您应该按articleID对数据进行分组并使用这些结果。您将什么定义为“慢”?您应该按articleID对数据进行分组并使用这些结果。您不会遇到选择n+1问题,只需要一个数据库请求,也不需要以这种方式说明缺少的文章。谢谢!这对我来说是最好的解决办法。我只需要添加一个close:var result=context.Articles.GroupJoin context.TrackingInformations,art=>art.Id,track=>track.ArticleId,x,y=>new{Id=x.Id,Count=y.Count}其中x=>cleanArticlesIds.Containsx.id.ToDictionarya=>a.id,a=>a.Count;是的,我和ilya在下面讨论了这个问题。你不会遇到选择n+1的问题,只有一个数据库请求,你也不需要这样解释丢失的文章。谢谢!这对我来说是最好的解决办法。我只需要添加一个close:var result=context.Articles.GroupJoin context.TrackingInformations,art=>art.Id,track=>track.ArticleId,x,y=>new{Id=x.Id,Count=y.Count}其中x=>cleanArticlesIds.Containsx.id.ToDictionarya=>a.id,a=>a.Count;是的,我和ilya在下面讨论了这一点。这不是一个坏方法,只是你最好做一个查询来检索跟踪信息,做第二个LINQ查询来检索缺少的文章,通过except,合并它们,最后转换成字典。这将导致两个数据库调用,完全是LINQ,但仍然没有我上面给出的答案(只有一个数据库调用)那么有效。@RobertMcKee在我的示例中,我只有一个数据库调用,添加missingArticleId根本不需要任何数据库调用。我假设CleanArticleSID是从数据库中检索的。@RobertMcKee Ask OP,不是我。若你们注意的话,他已经在第一行的代码中使用了它。我不知道他是不是从数据库里查到的。为什么不从用户界面?为什么不从另一个服务电话?我只是不知道,我想做尽可能少的假设,专注于一项特定的任务,尽可能接近作者的编码风格。这很公平,但如果你想假设CleanArticleSID没有从数据库中检索到,或者可能不是,或者可能不是所有的文章,然后,您应该在其中添加一个where,将从TrackingInformation提取的结果限制为CleanArticleID中的ArticleID,否则您将返回不在其列表中的文章。诚然,我也没有这样做,但我假设他想要一份所有文章的完整列表
t您可能最好只执行一次查询来检索跟踪信息,执行第二次LINQ查询来通过except检索缺少的文章,合并它们,最后转换为字典。这将导致两个数据库调用,完全是LINQ,但仍然没有我上面给出的答案(只有一个数据库调用)那么有效。@RobertMcKee在我的示例中,我只有一个数据库调用,添加missingArticleId根本不需要任何数据库调用。我假设CleanArticleSID是从数据库中检索的。@RobertMcKee Ask OP,不是我。若你们注意的话,他已经在第一行的代码中使用了它。我不知道他是不是从数据库里查到的。为什么不从用户界面?为什么不从另一个服务电话?我只是不知道,我想做尽可能少的假设,专注于一项特定的任务,尽可能接近作者的编码风格。这很公平,但如果你想假设CleanArticleSID没有从数据库中检索到,或者可能不是,或者可能不是所有的文章,然后,您应该在其中添加一个where,将从TrackingInformation提取的结果限制为CleanArticleID中的ArticleID,否则您将返回不在其列表中的文章。当然,我也没有这样做,但我认为他想要一份所有文章的完整列表。