一个Entity Framework Core的性能优化案例

概要

本文提供一个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, Email,UserName以及在1900年以前他们发行的图书信息,包括书名Name和发行日期Published。

基本优化思路

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

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

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

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

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

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

初始查询代码

csharp 复制代码
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, Book,User,Role,Publisher等多个数据表的数据全部载入,前面已经说了,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()方法。

csharp 复制代码
var orderedAuthors = authors.OrderByDescending(x => x.BooksCount).ToList().Take(2).ToList();

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

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

可以看到性略能有提升。

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

GetAuthorsOptimized_ChangeOrder方法生成的SQL如下:

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如下:

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对象。代码过于繁琐,而且带来的性能提升也不明显。因此放弃这个点。

csharp 复制代码
dbContext.Authors
   .AsNoTracking()
   .Include(x => x.Books.Where(b => b.Published < date))
   ......

结论

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

附录

实体类定义

csharp 复制代码
 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; }
     
 }

优化方法

csharp 复制代码
[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;
 }
相关推荐
黑狼传说14 小时前
前端项目优化:极致最优 vs 相对最优 —— 深入探索与实践
前端·性能优化
Lill_bin16 小时前
Lua编程语言简介与应用
开发语言·数据库·缓存·设计模式·性能优化·lua
人工智能培训咨询叶梓2 天前
MobiLlama,面向资源受限设备的轻量级全透明GPT模型
人工智能·gpt·语言模型·自然语言处理·性能优化·多模态·轻量级
Flying_Fish_roe2 天前
JVM 性能优化与调优-ZGC(Z Garbage Collector)
jvm·性能优化
Flying_Fish_roe2 天前
JVM 性能优化与调优-GraalVM
jvm·性能优化
四代水门3 天前
游戏性能优化
游戏·性能优化
安卓美女3 天前
Android自定义View性能优化
android·性能优化
Flying_Fish_roe3 天前
JVM 性能优化与调优-Shenandoah GC
jvm·性能优化
旺小仔.3 天前
【数据结构篇】~排序(1)之插入排序
c语言·数据结构·算法·链表·性能优化·排序算法