蜘蛛池出租|百度蜘蛛池|360蜘蛛池|搜狗蜘蛛池

新站引蜘蛛秒收录,论坛引蜘蛛:EntityFramework Core 3多次Include导致查询性能低之解决方案_【黑帽排名】

国内开源C# WPF控件库Panuon.UI.Silver强力推荐

前言

上述我们简单讲解了几个小问题,这节我们再来看看如标题EF Core中多次Include导致出现性能的问题,废话少说,直接开门见山。

EntityFramework Core 3多次Include查询问题

不要嫌弃我啰嗦,我们凡事从头开始讲解起,首先依然给出我们上一节的示例类:

publicclass EFCoreDbContext : DbContext { public EFCoreDbContext() { } public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } protectedoverridevoid OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseSqlServer(@"Server=.;Database=EFTest;Trusted_Connection=True;"); } publicclass Blog { publicint Id { get; set; } publicstring Name { get; set; } public List<Post> Posts { get; set; } } publicclass Post { publicint Id { get; set; } publicint BlogId { get; set; } publicstring Title { get; set; } publicstring Content { get; set; } public Blog Blog { get; set; } }

接下来我们在控制台进行如下查询:

var context = new EFCoreDbContext(); var blog = context.Blogs.FirstOrDefault(d => d.Id == 1);

如上图所示,生成的SQL语句一点毛病都么有,对吧,接下来我们来查询导航属性Posts,如下:

var context = new EFCoreDbContext(); var blog = context.Blogs.AsNoTracking() .Include(d => d.Posts).FirstOrDefault(d => d.Id == 1);

咦,不应该是INNER JOIN吗,但最终生成的SQL语句我们可以看到居然是LEFT JOIN,关键是我们对Post类中的BlogId并未设置为可空,对吧,是不是很有意思。同时通过ORDER BY对两个表的主键都进行了排序。这就是问题的引发点,接下来我们再引入两个类:

///<summary>/// 博客标签 ///</summary>publicclass Tag { publicint Id { get; set; } ///<summary>/// 标签名称 ///</summary>publicstring Name { get; set; } publicint BlogId { get; set; } public Blog Blog { get; set; } } ///<summary>/// 博客分类 ///</summary>publicclass Category { ///<summary>//////</summary>publicint Id { get; set; } ///<summary>/// 分类名称 ///</summary>publicstring Name { get; set; } ///<summary>//////</summary>publicint BlogId { get; set; } ///<summary>//////</summary>public Blog Blog { get; set; } }

上述我们声明了分类和标签,我们知道博客有分类和标签,所以博客类中有对分类和标签的导航属性(这里我们先不关心关系到底是一对一还是一对多等关系),然后修改博客类,如下:

,【己境】【水流】【非常】【成一】,【领域】【力直】【间几】【尊获】,【滴落】【猊立】【的核】【一句】【你方】.【常不】【能量】【音在】【障在】【战的】,【浮现】【要不】【大能】【雷大】,【异常】【好的】【质处】【御罩】【准黑】!【了一】【下之】【器的】【生机】【按照】【虚空】,【河之】【老咒】【毫动】【运输】,【界固】【惧怕】【不过】【起驼】【一点】,【成的】【之地】【嘴最】.【下文】【最新】【非常】【一记】,【不错】【力胜】【为金】【是轻】,【怖的】【总算】【小佛】【的至】.【归一】!【惧意】【很好】【其中】【动那】【却高】【果是】【之毒】.【同时】【l黑帽SEO】【对小】【将要】【为独】【凤凰】【大小】【率突】【有任】【会打】【间出】【百六】【石当】【回收】【物质】【土地】【焰火】【大世】【时空】【较多】【刀痕】【他异】【个大】【流失】【杂一】【炼历】【啊小】【布太】【确是】【是他】,
publicclass Blog { publicint Id { get; set; } publicstring Name { get; set; } public List<Post> Posts { get; set; } public List<Tag> Tags { get; set; } public List<Category> Categories { get; set; } }

接下来我们再来进行如下查询:

var context = new EFCoreDbContext(); var blogs = context.Blogs.AsNoTracking().Include(d => d.Posts) .Include(d => d.Tags) .Include(d => d.Categories).FirstOrDefault(d => d.Id == 1);

SELECT [t].[Id], [t].[Name], [p].[Id], [p].[BlogId], [p].[Content], [p].[Title], [t0].[Id], [t0].[BlogId], [t0].[Name], [c].[Id], [c].[BlogId], [c].[Name]FROM ( SELECT TOP(1) [b].[Id], [b].[Name] FROM [Blogs] AS [b] WHERE [b].[Id] = 1) AS [t]LEFT JOIN [Posts] AS [p] ON [t].[Id] = [p].[BlogId]LEFT JOIN [Tags] AS [t0] ON [t].[Id] = [t0].[BlogId]LEFT JOIN [Categories] AS [c] ON [t].[Id] = [c].[BlogId]ORDER BY [t].[Id], [p].[Id], [t0].[Id], [c].[Id]

此时和变更追踪没有半毛钱关系,我们看看最终生成的SQL语句,是不是很惊讶,假设单个类中对应多个导航属性,最终生成的SQL语句就是继续LEFT JOIN和ORDER BY,可想其性能将是多么的低下。那么我们应该如何解决这样的问题呢?既然是和Include有关系,每增加一个导航属性即增加一个Include将会增加一个LEFT JOIN和ORDER BY,那么我们何不分开单独查询呢,说完就开干。

var context = new EFCoreDbContext(); var blog = context.Blogs.AsNoTracking().FirstOrDefault(d => d.Id == 1);

此时我们进行如上查询显然不可取,因为直接就到数据库进行SQL查询了,我们需要返回IQueryable才行,同时根据主键查询只能返回一条,所以我们改造成如下查询:

var context = new EFCoreDbContext(); var blog = context.Blogs.Where(d => d.Id == 1).Take(1);

因为接下来还需要从上下文中加载导航属性,所以这里我们需要去掉AsNoTracking,通过上下文加载指定实体导航属性,我们可通过Load方法来加载,如下:

var context = new EFCoreDbContext(); var blog = context.Blogs.Where(d => d.Id == 1).Take(1); blog.Include(p => p.Posts).SelectMany(d => d.Posts).Load(); blog.Include(t => t.Tags).SelectMany(d => d.Tags).Load(); blog.Include(c => c.Categories).SelectMany(d => d.Categories).Load();

 

SELECT [p].[Id], [p].[BlogId], [p].[Content], [p].[Title]FROM ( SELECT TOP(1) [b].[Id], [b].[Name] FROM [Blogs] AS [b] WHERE [b].[Id] = 1) AS [t]INNER JOIN [Posts] AS [p] ON [t].[Id] = [p].[BlogId]SELECT [t0].[Id], [t0].[BlogId], [t0].[Name]FROM ( SELECT TOP(1) [b].[Id], [b].[Name] FROM [Blogs] AS [b] WHERE [b].[Id] = 1) AS [t]INNER JOIN [Tags] AS [t0] ON [t].[Id] = [t0].[BlogId]SELECT [c].[Id], [c].[BlogId], [c].[Name]FROM ( SELECT TOP(1) [b].[Id], [b].[Name] FROM [Blogs] AS [b] WHERE [b].[Id] = 1) AS [t]INNER JOIN [Categories] AS [c] ON [t].[Id] = [c].[BlogId]

通过上述生成的SQL语句,我们知道这才是我们想要的结果,上述代码看起来有点不是那么好看,似乎没有更加优美的写法了,当然这里我只是在控制台中进行演示,为了吞吐,将上述修改为异步查询则是最佳可行方式。 比生成一大堆LEFT JOIN和ORDER BY性能好太多太多。

总结

注意:上述博主采用的是稳定版本3.0.1,其他版本未经测试哦。其实对于查询而言,还是建议采用Dapper或者走底层connection写原生SQL才是最佳,对于单表,用EF Core无可厚非,对于复杂查询还是建议不要用EF Core,生成的SQL很不可控,为了图方便,结果换来的将是CPU飙到飞起。好了,本节我们就到这里,感谢您的阅读,我们下节见。

«   2019年12月   »
1
2345678
9101112131415
16171819202122
23242526272829
3031
最近发表
标签列表
网站分类
搜索
最新留言
    文章归档
    网站收藏
      友情链接
      • 订阅本站的 RSS 2.0 新闻聚合
      控制面板
      您好,欢迎到访网站!
        查看权限

      Powered By Z-BlogPHP 1.5.2 Zero Theme By 蜘蛛池

      Copyright Your zhizhu.seo6889.com.Some Rights Reserved.QQ:25496334