一个Entity Framework Core的性能优化案例-CSDN博客

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

概要

本文提供一个EF Core的优化案例主要介绍一些EF Core常用的优化方法以及在优化过程中出现性能反复的时候的解决方法并澄清一些对优化概念的误解例如AsNoTracking并不包治百病。

本文使用的是Dotnet 6.0和EF Core 7.0。

代码及实现

背景介绍

本文主要使用一个图书和作者的案例用于介绍优化过程。

  • 一个作者Author有多本自己写的的图书Book
  • 一本图书Book有一个发行商Publisher
  • 一个作者Author是一个系统用户User
  • 一个系统用户User有多个角色Role

本实例中Author表和Book数据量较大记录数量全部过万条其它数据表记录大概都是几十或几百条。具体实体类定义请见附录。

查询需求

我们需要查找写书最多的前两名作家该作家需要年龄在20岁以上国籍是法国。需要他们的FirstName LastName EmailUserName以及在1900年以前他们发行的图书信息包括书名Name和发行日期Published。

基本优化思路

本人做EF Core的复杂查询优化并不推荐直接查看生成的SQL代码我习惯按照如下方式进行

首先进行EF的LINQ代码检查初筛找到明显的错误。

  1. 查看代码中是否有基本错误主要针对全表载入的问题。例如EF需要每一步的LINQ扩展方法的返回值都是IQueryable类型不能有IEnumerable类型
  2. 查看是否有不需要的栏位
  3. 根据情况决定是否加AsNoTracking注意这个东西有时候加了也没用

其次找到数据量较大的表进行代码整理和针对大数据表的优化精细化调整

  1. 在操作大数据表时候先要进行基本的过滤
  2. 投影操作Select应该放到排序操作后面
  3. 减少返回值数量推荐进行分页操作

本人推荐一旦出现性能反复的时候或者代码整体基本完成的时候再去查看生成的SQL代码。

初始查询代码

public  List<AuthorWeb> GetAuthors() {
     using var dbContext = new AppDbContext();
     var authors = dbContext.Authors
                 .Include(x => x.User)
                  .ThenInclude(x => x.UserRoles)
                  .ThenInclude(x => x.Role)
                  .Include(x => x.Books)
                  .ThenInclude(x => x.Publisher)
                  .ToList()
                  .Select(x => new AuthorWeb
                  {
                      UserCreated = x.User.Created,
                      UserEmailConfirmed = x.User.EmailConfirmed,
                      UserFirstName = x.User.FirstName,
                      UserLastActivity = x.User.LastActivity,
                      UserLastName = x.User.LastName,
                      UserEmail = x.User.Email,
                      UserName = x.User.UserName,
                      UserId = x.User.Id,
                      RoleId = x.User.UserRoles.FirstOrDefault(y => y.UserId == x.UserId).RoleId,
                      BooksCount = x.BooksCount,
                      AllBooks = x.Books.Select(y => new BookWeb
                      {
                          Id = y.Id,
                          Name = y.Name,
                          Published = y.Published,
                          ISBN = y.ISBN,
                          PublisherName = y.Publisher.Name
                      }).ToList(),
                      AuthorAge = x.Age,
                      AuthorCountry = x.Country,
                      AuthorNickName = x.NickName,
                      Id = x.Id
                  })
                  .ToList()
                  .Where(x => x.AuthorCountry == "France" && x.AuthorAge == 20)
                  .ToList();

     var orderedAuthors = authors.OrderByDescending(x => x.BooksCount).ToList().Take(2).ToList();

     List<AuthorWeb> finalAuthors = new List<AuthorWeb>();
     foreach (var author in orderedAuthors)
     {
         List<BookWeb> books = new List<BookWeb>();

         var allBooks = author.AllBooks;

         foreach (var book in allBooks)
         {
             if (book.Published.Year < 1900)
             {
                 book.PublishedYear = book.Published.Year;
                 books.Add(book);
             }
         }

         author.AllBooks = books;
         finalAuthors.Add(author);
     }

     return finalAuthors;
 }

Benchmark测试后系统资源使用情况如下

在这里插入图片描述

代码性能非常差内存消耗很大一次执行就要消耗190MB执行时间超过2s。如果是放到WebAPI里面调用用户会有明显的卡顿感觉如果面临高并发的情况很可得会造成服务器资源紧张返回各种500错误。

优化代码

初筛

按照我们的优化思路在查看上面的代码后发现一个严重的问题。

虽然每次LINQ查询返回都是IQueryable类型但是源码中有多个ToList()尤其是第一个它的意思是将Author BookUserRolePublisher等多个数据表的数据全部载入前面已经说了Author Book两张表的数据量很大必然影响性能。

我们需要删除前面多余的ToList()只保留最后的即可。请参考附录中的方法GetAuthors_RemoveToList()。

在GetAuthors_RemoveToList()基础上对照用户的需求发现查询结果中包含了Role相关的信息和很多Id信息但是查询结果并不需要这些因此必须删掉。请参考附录中的方法GetAuthorsOptimized_RemoveColumn()

在GetAuthorsOptimized_RemoveColumn的基础上我们再加入AsNoTracking方法。请参考附录中的方法GetAuthorsOptimized_AsNoTracking()

我们在Benchmark中测试上面提到的三个方法直接结果如下

在这里插入图片描述

从Benchmark的测试结果上看删除多余ToList方法和删除多余的栏位确实带来了性能的大幅提升。

但是增加AsNoTracking性能提反而下降了一点。这也说明了AsNoTracking并不是适用所有场景。Select投影操作生成的AuthorWeb对像并不是EF管理的与DbContext无关它只是作为前端API的返回值。相当于EF做了没有用的事所以性能略有下降。

代码进一步调整

初筛阶段完成后下面对代码进一步整理

下面Take和Order操作可以并入基本的查询中Take可以帮助我们减少返回值的数量。请见 GetAuthorsOptimized_ChangeOrder()方法。

var orderedAuthors = authors.OrderByDescending(x => x.BooksCount).ToList().Take(2).ToList();

在GetAuthorsOptimized_ChangeOrder基础上对于dbContext.AuthorsAuthor是一张数据量很大的表我们需要在其进行联表操作前先过滤掉不需要的内容所以我们可以把Where前提还有就是将排序操作放到投影的Select前面完成。请见 GetAuthorsOptimized_ChangeOrder方法。

上面的两个优化方法的执行结果如下

在这里插入图片描述
可以看到性略能有提升。

下面我们为了进一步提升性能可以查看一下生成的SQL代码看看是否还有优化的空间。

GetAuthorsOptimized_ChangeOrder方法生成的SQL如下

      SELECT [u].[FirstName], [u].[LastName], [u].[Email], [u].[UserName], [t].[
BooksCount], [t].[Id], [u].[Id], [b].[Name], [b].[Published], [b].[Id], [t].[Age
], [t].[Country]
      FROM (
          SELECT TOP(@__p_0) [a].[Id], [a].[Age], [a].[BooksCount], [a].[Country
], [a].[UserId]
          FROM [Authors] AS [a]
          WHERE [a].[Country] = N'France' AND [a].[Age] >= 20
          ORDER BY [a].[BooksCount] DESC
      ) AS [t]
      INNER JOIN [Users] AS [u] ON [t].[UserId] = [u].[Id]
      LEFT JOIN [Books] AS [b] ON [t].[Id] = [b].[AuthorId]
      ORDER BY [t].[BooksCount] DESC, [t].[Id], [u].[Id]

从生成SQL来看Author表在使用之前过滤掉了相关的内容但是直接Left Join了[Books]这个大表。我们可以按照前面提到的1900年以前的查询要求在左联之前先过滤一下请参考 GetAuthorsOptimized_SelectFilter方法。

该方法执行后生成的SQL如下

      SELECT [u].[FirstName], [u].[LastName], [u].[Email], [u].[UserName], [t].[
BooksCount], [t].[Id], [u].[Id], [t0].[Name], [t0].[Published], [t0].[Id], [t].[
Age], [t].[Country]
      FROM (
          SELECT TOP(@__p_1) [a].[Id], [a].[Age], [a].[BooksCount], [a].[Country
], [a].[UserId]
          FROM [Authors] AS [a]
          WHERE [a].[Country] = N'France' AND [a].[Age] >= 20
          ORDER BY [a].[BooksCount] DESC
      ) AS [t]
      INNER JOIN [Users] AS [u] ON [t].[UserId] = [u].[Id]
      LEFT JOIN (
          SELECT [b].[Name], [b].[Published], [b].[Id], [b].[AuthorId]
          FROM [Books] AS [b]
          WHERE [b].[Published] < @__date_0
      ) AS [t0] ON [t].[Id] = [t0].[AuthorId]
      ORDER BY [t].[BooksCount] DESC, [t].[Id], [u].[Id]

在左联之前确实进行了过滤该方法的性能测试如下

在这里插入图片描述
在避免Book表直接进行左联后性能有所提升。

最后一个优化点是在EF Core 5.0里面提供了带Filter功能的Include方法也可以用于本案例的优化但是该特性但是存在一些局限性具体请参考EF Core中带过滤器参数的Include方法

但是此方法又涉及了将IQueryable转换成IEnumerable的操作最后要将生成的Author对象全部转换成AuthorWeb对象。代码过于繁琐而且带来的性能提升也不明显。因此放弃这个点。

dbContext.Authors
   .AsNoTracking()
   .Include(x => x.Books.Where(b => b.Published < date))
   ......

结论

从这个优化过程来看其实对性能提升最大的贡献就是删除多余的ToList()避免全表载入和删除不需要的栏位两项。其它所谓更精细的优化性能提升有限。

附录

实体类定义

 public class Author
    {
        public int Id { get; set; }
        public int Age { get; set; }
        public string Country { get; set; }
        public int BooksCount { get; set; }
        public string NickName { get; set; }

        [ForeignKey("UserId")]
        public User User { get; set; }
        public int UserId { get; set; }
        public virtual List<Book> Books { get; set; } = new List<Book>();
    }
 public class Book
    {
        public int Id { get; set; }
        public string Name { get; set; }
        [ForeignKey("AuthorId")]
        public Author Author { get; set; }
        public int AuthorId { get; set; }
        public DateTime Published { get; set; }
        public string ISBN { get; set; }
        [ForeignKey("PublisherId")]
        public Publisher Publisher { get; set; }
        public int PublisherId { get; set; }
    }
public class Publisher
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime Established { get; set; }
    }
     public class User
    {
        public int Id { get; set; }

        public string FirstName { get; set; }

        public string LastName { get; set; }

        public string UserName { get; set; }
        public string Email { get; set; }
        public virtual List<UserRole> UserRoles { get; set; } = new List<UserRole>();
        public DateTime Created { get; set; }
        public bool EmailConfirmed { get; set; }
        public DateTime LastActivity { get; set; }
    }
    public class Role
    {
        public int Id { get; set; }
        public virtual List<UserRole> UserRoles { get; set; } = new List<UserRole>();
        public string Name { get; set; }
    }

public  class AuthorWeb
 {
     public DateTime UserCreated { get; set; }
     public bool UserEmailConfirmed { get; set; }
     public string UserFirstName { get; set; }
     public DateTime UserLastActivity { get; set; }
     public string UserLastName { get; set; }
     public string UserEmail { get; set; }
     public string UserName { get; set; }
     public int UserId { get; set; }
     public int AuthorId { get; set; }
     public int Id { get; set; }
     public int RoleId { get; set; }
     public int BooksCount { get; set; }
     public List<BookWeb> AllBooks { get; set; }
     public int AuthorAge { get; set; }
     public string AuthorCountry { get; set; }
     public string AuthorNickName { get; set; }
 }
 public class BookWeb
 {
         public int Id { get; set; }
         public string Name { get; set; }
         public DateTime Published { get; set; }
         public int PublishedYear { get; set; }
         public string PublisherName { get; set; }
         public string ISBN { get; set; }
     
 }

优化方法

[Benchmark]
public  List<AuthorWeb> GetAuthors() {
     using var dbContext = new AppDbContext();

     var authors = dbContext.Authors
                                 .Include(x => x.User)
                                 .ThenInclude(x => x.UserRoles)
                                 .ThenInclude(x => x.Role)
                                 .Include(x => x.Books)
                                 .ThenInclude(x => x.Publisher)
                                 .ToList()
                                 .Select(x => new AuthorWeb
                                 {
                                     UserCreated = x.User.Created,
                                     UserEmailConfirmed = x.User.EmailConfirmed,
                                     UserFirstName = x.User.FirstName,
                                     UserLastActivity = x.User.LastActivity,
                                     UserLastName = x.User.LastName,
                                     UserEmail = x.User.Email,
                                     UserName = x.User.UserName,
                                     UserId = x.User.Id,
                                     RoleId = x.User.UserRoles.FirstOrDefault(y => y.UserId == x.UserId).RoleId,
                                     BooksCount = x.BooksCount,
                                     AllBooks = x.Books.Select(y => new BookWeb
                                     {
                                         Id = y.Id,
                                         Name = y.Name,
                                         Published = y.Published,
                                         ISBN = y.ISBN,
                                         PublisherName = y.Publisher.Name
                                     }).ToList(),
                                     AuthorAge = x.Age,
                                     AuthorCountry = x.Country,
                                     AuthorNickName = x.NickName,
                                     Id = x.Id
                                 })
                                 .ToList()
                                 .Where(x => x.AuthorCountry == "France" && x.AuthorAge >= 20)
                                 .ToList();

     var orderedAuthors = authors.OrderByDescending(x => x.BooksCount).ToList().Take(2).ToList();

     List<AuthorWeb> finalAuthors = new List<AuthorWeb>();
     foreach (var author in orderedAuthors)
     {
         List<BookWeb> books = new List<BookWeb>();

         var allBooks = author.AllBooks;

         foreach (var book in allBooks)
         {
             if (book.Published.Year < 1900)
             {
                 book.PublishedYear = book.Published.Year;
                 books.Add(book);
             }
         }

         author.AllBooks = books;
         finalAuthors.Add(author);
     }

     return finalAuthors;
 }

 [Benchmark]
 public List<AuthorWeb> GetAuthors_RemoveToList()
 {
     using var dbContext = new AppDbContext();

     var authors = dbContext.Authors
                                 .Include(x => x.User)
                                 .ThenInclude(x => x.UserRoles)
                                 .ThenInclude(x => x.Role)
                                 .Include(x => x.Books)
                                 .ThenInclude(x => x.Publisher)
                               //  .ToList()
                                 .Select(x => new AuthorWeb
                                 {
                                     UserCreated = x.User.Created,
                                     UserEmailConfirmed = x.User.EmailConfirmed,
                                     UserFirstName = x.User.FirstName,
                                     UserLastActivity = x.User.LastActivity,
                                     UserLastName = x.User.LastName,
                                     UserEmail = x.User.Email,
                                     UserName = x.User.UserName,
                                     UserId = x.User.Id,
                                     RoleId = x.User.UserRoles.FirstOrDefault(y => y.UserId == x.UserId).RoleId,
                                     BooksCount = x.BooksCount,
                                     AllBooks = x.Books.Select(y => new BookWeb
                                     {
                                         Id = y.Id,
                                         Name = y.Name,
                                         Published = y.Published,
                                         ISBN = y.ISBN,
                                         PublisherName = y.Publisher.Name
                                     }).ToList(),
                                     AuthorAge = x.Age,
                                     AuthorCountry = x.Country,
                                     AuthorNickName = x.NickName,
                                     Id = x.Id
                                 })
                                // .ToList()
                                 .Where(x => x.AuthorCountry == "France" && x.AuthorAge >=20)
                                 .ToList();

     var orderedAuthors = authors.OrderByDescending(x => x.BooksCount).ToList().Take(2).ToList();

     List<AuthorWeb> finalAuthors = new List<AuthorWeb>();
     foreach (var author in orderedAuthors)
     {
         List<BookWeb> books = new List<BookWeb>();

         var allBooks = author.AllBooks;

         foreach (var book in allBooks)
         {
             if (book.Published.Year < 1900)
             {
                 book.PublishedYear = book.Published.Year;
                 books.Add(book);
             }
         }

         author.AllBooks = books;
         finalAuthors.Add(author);
     }

     return finalAuthors;
 }
 [Benchmark]
 public  List<AuthorWeb> GetAuthorsOptimized_RemoveColumn()
 {
     using var dbContext = new AppDbContext();

     var authors = dbContext.Authors
                              //   .Include(x => x.User)
                                 //.ThenInclude(x => x.UserRoles)
                              //   .ThenInclude(x => x.Role)
                              //   .Include(x => x.Books)
                            //     .ThenInclude(x => x.Publisher)
                                 .Select(x => new AuthorWeb
                                 {
                                  //   UserCreated = x.User.Created,
                                   //  UserEmailConfirmed = x.User.EmailConfirmed,
                                     UserFirstName = x.User.FirstName,
                                  //   UserLastActivity = x.User.LastActivity,
                                     UserLastName = x.User.LastName,
                                     UserEmail = x.User.Email,
                                     UserName = x.User.UserName,
                                //     UserId = x.User.Id,
                                //     RoleId = x.User.UserRoles.FirstOrDefault(y => y.UserId == x.UserId).RoleId,
                                     BooksCount = x.BooksCount,
                                     AllBooks = x.Books.Select(y => new BookWeb
                                     {
                                     //    Id = y.Id,
                                         Name = y.Name,
                                         Published = y.Published,
                                  //       ISBN = y.ISBN,
                                //         PublisherName = y.Publisher.Name
                                     }).ToList(),
                                     AuthorAge = x.Age,
                                     AuthorCountry = x.Country,
                                     AuthorNickName = x.NickName,
                                  //   Id = x.Id
                                 })
                                 .Where(x => x.AuthorCountry == "France" && x.AuthorAge >=20)
                                 .ToList();

     var orderedAuthors = authors.OrderByDescending(x => x.BooksCount).ToList().Take(2).ToList();

     List<AuthorWeb> finalAuthors = new List<AuthorWeb>();
     foreach (var author in orderedAuthors)
     {
         List<BookWeb> books = new List<BookWeb>();

         var allBooks = author.AllBooks;

         foreach (var book in allBooks)
         {
             if (book.Published.Year < 1900)
             {
                 book.PublishedYear = book.Published.Year;
                 books.Add(book);
             }
         }

         author.AllBooks = books;
         finalAuthors.Add(author);
     }

     return finalAuthors;
 }

[Benchmark]
 public List<AuthorWeb> GetAuthorsOptimized_AsNoTracking()
 {
     using var dbContext = new AppDbContext();

     var authors = dbContext.Authors
                                 .AsNoTracking()
                                 // .Include(x => x.User)
                                 //   .ThenInclude(x => x.UserRoles)
                                 //   .ThenInclude(x => x.Role)
                                 //    .Include(x => x.Books)
                                 //   .ThenInclude(x => x.Publisher)
                                 .Select(x => new AuthorWeb
                                 {
                                     //UserCreated = x.User.Created,
                                     //    UserEmailConfirmed = x.User.EmailConfirmed,
                                     UserFirstName = x.User.FirstName,
                                     // UserLastActivity = x.User.LastActivity,
                                     UserLastName = x.User.LastName,
                                     UserEmail = x.User.Email,
                                     UserName = x.User.UserName,
                                     //  UserId = x.User.Id,
                                     //RoleId = x.User.UserRoles.FirstOrDefault(y => y.UserId == x.UserId).RoleId,
                                     BooksCount = x.BooksCount,
                                     AllBooks = x.Books.Select(y => new BookWeb
                                     {
                                         // Id = y.Id,
                                         Name = y.Name,
                                         Published = y.Published,
                                         //ISBN = y.ISBN,
                                         //PublisherName = y.Publisher.Name
                                     }).ToList(),
                                     AuthorAge = x.Age,
                                     AuthorCountry = x.Country,
                                     //AuthorNickName = x.NickName,
                                     Id = x.Id
                                 })
                                 .Where(x => x.AuthorCountry == "France" && x.AuthorAge >=20)
                                 .ToList();

      var orderedAuthors = authors.OrderByDescending(x => x.BooksCount).ToList().ToList();

     List<AuthorWeb> finalAuthors = new List<AuthorWeb>();
     foreach (var author in authors)
     {
         List<BookWeb> books = new List<BookWeb>();

         var allBooks = author.AllBooks;

         foreach (var book in allBooks)
         {
             if (book.Published.Year < 1900)
             {
                 book.PublishedYear = book.Published.Year;
                 books.Add(book);
             }
         }

         author.AllBooks = books;
         finalAuthors.Add(author);
     }

     return finalAuthors;
 }


 [Benchmark]
 public List<AuthorWeb> GetAuthorsOptimized_Take_Order()
 {
     using var dbContext = new AppDbContext();

     var authors = dbContext.Authors
                                 .AsNoTracking()
                                 .Select(x => new AuthorWeb
                                 {
                                     UserFirstName = x.User.FirstName,
                                     UserLastName = x.User.LastName,
                                     UserEmail = x.User.Email,
                                     UserName = x.User.UserName,
                                     BooksCount = x.BooksCount,
                                     AllBooks = x.Books.Select(y => new BookWeb
                                     {
                                         Name = y.Name,
                                         Published = y.Published,
                                     }).ToList(),
                                     AuthorAge = x.Age,
                                     AuthorCountry = x.Country,
                                     AuthorNickName = x.NickName,
                                 })
                                 .Where(x => x.AuthorCountry == "France" && x.AuthorAge >=20)
                                 .OrderByDescending(x => x.BooksCount)
                                 .Take(2)
                                 .ToList();

    // var orderedAuthors = authors.OrderByDescending(x => x.BooksCount).ToList().ToList();

     List<AuthorWeb> finalAuthors = new List<AuthorWeb>();
     foreach (var author in authors)
     {
         List<BookWeb> books = new List<BookWeb>();

         var allBooks = author.AllBooks;

         foreach (var book in allBooks)
         {
             if (book.Published.Year < 1900)
             {
                 book.PublishedYear = book.Published.Year;
                 books.Add(book);
             }
         }

         author.AllBooks = books;
         finalAuthors.Add(author);
     }

     return finalAuthors;
 }


 [Benchmark]
 public List<AuthorWeb> GetAuthorsOptimized_ChangeOrder()
 {
     using var dbContext = new AppDbContext();

     var authors = dbContext.Authors
                                  .AsNoTracking()
                                  .Where(x => x.Country == "France" && x.Age >=20)
                                  .OrderByDescending(x => x.BooksCount)
                                 // .Include(x => x.User)
                                 //   .ThenInclude(x => x.UserRoles)
                                 //   .ThenInclude(x => x.Role)
                                 //    .Include(x => x.Books)
                                 //   .ThenInclude(x => x.Publisher)
                                 .Select(x => new AuthorWeb
                                 {
                                     //UserCreated = x.User.Created,
                                     //    UserEmailConfirmed = x.User.EmailConfirmed,
                                     UserFirstName = x.User.FirstName,
                                     // UserLastActivity = x.User.LastActivity,
                                     UserLastName = x.User.LastName,
                                     UserEmail = x.User.Email,
                                     UserName = x.User.UserName,
                                     //  UserId = x.User.Id,
                                     //RoleId = x.User.UserRoles.FirstOrDefault(y => y.UserId == x.UserId).RoleId,
                                     BooksCount = x.BooksCount,
                                     AllBooks = x.Books.Select(y => new BookWeb
                                     {
                                         // Id = y.Id,
                                         Name = y.Name,
                                         Published = y.Published,
                                         //ISBN = y.ISBN,
                                         //PublisherName = y.Publisher.Name
                                     }).ToList(),
                                     AuthorAge = x.Age,
                                     AuthorCountry = x.Country,
                                     //AuthorNickName = x.NickName,
                                     Id = x.Id
                                 })                                     
                                 .Take(2)
                                 .ToList();

     // var orderedAuthors = authors.OrderByDescending(x => x.BooksCount).ToList().ToList();

     List<AuthorWeb> finalAuthors = new List<AuthorWeb>();
     foreach (var author in authors)
     {
         List<BookWeb> books = new List<BookWeb>();

         var allBooks = author.AllBooks;

         foreach (var book in allBooks)
         {
             if (book.Published.Year < 1900)
             {
                 book.PublishedYear = book.Published.Year;
                 books.Add(book);
             }
         }

         author.AllBooks = books;
         finalAuthors.Add(author);
     }

     return finalAuthors;
 }

//  [Benchmark]
 public List<AuthorWeb> GetAuthorsOptimized_IncludeFilter()
 {
     using var dbContext = new AppDbContext();
     var date = new DateTime(1900, 1, 1);
     var authors = dbContext.Authors
                                 .AsNoTracking()
                                 .Include(x => x.Books.Where(b => b.Published < date))
                                 .Include(x => x.User)
                                  // .IncludeFilter(x =>x.Books.Where(b =>b.Published.Year < 1900))
                                  .Where(x => x.Country == "France" && x.Age >=20)
                                  .OrderByDescending(x => x.BooksCount)
                              
                                 //   .ThenInclude(x => x.UserRoles)
                                 //   .ThenInclude(x => x.Role)
                                 //    .Include(x => x.Books)
                                 //   .ThenInclude(x => x.Publisher)
                                 .Take(2)
                                 .ToList();

     // var orderedAuthors = authors.OrderByDescending(x => x.BooksCount).ToList().ToList();

     var authorList = authors.AsEnumerable().Select(x => new AuthorWeb
     {
         //UserCreated = x.User.Created,
         //    UserEmailConfirmed = x.User.EmailConfirmed,
         UserFirstName = x.User.FirstName,
         // UserLastActivity = x.User.LastActivity,
         UserLastName = x.User.LastName,
         UserEmail = x.User.Email,
         UserName = x.User.UserName,
         //  UserId = x.User.Id,
         //RoleId = x.User.UserRoles.FirstOrDefault(y => y.UserId == x.UserId).RoleId,
         BooksCount = x.BooksCount,
         AllBooks = x.Books
                                        //    .Where(b => b.Published < date)
                                        .Select(y => new BookWeb
                                        {
                                            // Id = y.Id,
                                            Name = y.Name,
                                            Published = y.Published,
                                            //ISBN = y.ISBN,
                                            //PublisherName = y.Publisher.Name
                                        }).ToList(),
         AuthorAge = x.Age,
         AuthorCountry = x.Country,
         //AuthorNickName = x.NickName,
         //    Id = x.Id
     }).ToList();

     return authorList;
 }


 [Benchmark]
 public List<AuthorWeb> GetAuthorsOptimized_SelectFilter()
 {
     using var dbContext = new AppDbContext();
     var date = new DateTime(1900, 1, 1);
     var authors = dbContext.Authors
                                 .AsNoTracking()
                                 .Include(x => x.Books.Where(b => b.Published < date))
                                 .Where(x => x.Country == "France" && x.Age >=20)
                                 .OrderByDescending(x => x.BooksCount)
                                 .Select(x => new AuthorWeb
                                  {
                                      //UserCreated = x.User.Created,
                                      //    UserEmailConfirmed = x.User.EmailConfirmed,
                                      UserFirstName = x.User.FirstName,
                                      // UserLastActivity = x.User.LastActivity,
                                      UserLastName = x.User.LastName,
                                      UserEmail = x.User.Email,
                                      UserName = x.User.UserName,
                                      //  UserId = x.User.Id,
                                      //RoleId = x.User.UserRoles.FirstOrDefault(y => y.UserId == x.UserId).RoleId,
                                      BooksCount = x.BooksCount,
                                      AllBooks = x.Books
                                         .Where(b => b.Published < date)
                                        .Select(y => new BookWeb
                                        {
                                            // Id = y.Id,
                                            Name = y.Name,
                                            Published = y.Published,
                                            //ISBN = y.ISBN,
                                            //PublisherName = y.Publisher.Name
                                        }).ToList(),
                                      AuthorAge = x.Age,
                                      AuthorCountry = x.Country,
                                      //AuthorNickName = x.NickName,
                                      //    Id = x.Id
                                  })
                                 .Take(2)
                                 .ToList();
     return authors;
 }
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6