Code2Gether-Discord / JokesOnYou

A learning project, A jokes website build as a team project.
12 stars 11 forks source link

Reduce the amount of queries made to the database and only select the necessary data #196

Closed chrisK00 closed 2 years ago

chrisK00 commented 2 years ago

The problem in JokeService

  1. For every joke that has an author, the author will be loaded. An author is retrieved n times because we don't cache the author since we are using .AsNoTracking
  2. We load the whole user entity event though we only want the username

The solution in JokesRepository

  1. Inside GetJokeDtosAsync we know that this will return a JokeReplyDto which has an author entity, that means we can directly populate this here.
  2. We can start defining the query var query = _context.Jokes.AsNoTracking(); // asnotracking will make it a queryable
  3. We do our filters on the query such has if (!string.IsNullOrWhiteSpace(jokesFilter.AuthorId)) { query = query.Where(joke => joke.AuthorId == jokesFilter.AuthorId); }
  4. We paginate the query which means we need to refactor our PaginatedList to return the iqueryable instead of executing it so we later on can execute the .ToPaginatedList var jokes = await query.Paginate(int pageSize)
  5. Now we need to load the relatedAuthors and we can use the query to only make one request to the db by doing so we are making a SELECTsubquery instead of a Joinwhich becomes more performant. var authors = await _context.Users.Where(u => query.Select(j => j.AuthorId).Contains(u.Id)) .Select(x => new { x.UserName, x.Id }).ToListAsync()
  6. Almost done, just need to create the ReplyDto return jokes.Select(j => new JokeReplyDto { Author = new JokeAuthorDto { Id = j.AuthorId, UserName = authors.FirstOrDefault(x => x.Id == j.AuthorId).UserName }, });

What have we changed?