Loading nested properties in Entity Framework Core

c# entity-framework-core

Question

Is there any way I can avoid using Include and ThenInclude in EF Core ? I have these models and dtos :

For Book:

  public partial class Book
   {
      public Book()
   {
     BookAuthors = new HashSet<BookAuthor>();
     BookCategories = new HashSet<BookCategory>();
     Reviews = new HashSet<Review>();
   }

  public int BookId { get; set; }
  public string Title { get; set; }
  ...
  public string ImageUrl { get; set; }

  public ICollection<BookAuthor> BookAuthors { get; set; }
  public ICollection<BookCategory> BookCategories { get; set; }
  public ICollection<Review> Reviews { get; set; }
 }

  public class BookDto
 {
   public int BookId { get; set; }
   public string Title { get; set; }
   ...
   public string ImageUrl { get; set; }
   public IList<AuthorDto> Authors { get; set; }
   public IList<CategoryDto> Categories { get; set; }
   public IList<ReviewDto> Reviews { get; set; }
}

For Author :

public partial class Author
{
    public Author()
    {
        BookAuthors = new HashSet<BookAuthor>();
    }
    public int AuthorId { get; set; }
    public string AuthorName { get; set; }
    ...
    public ICollection<BookAuthor> BookAuthors { get; set; }
}

public class AuthorDto
{
  public int AuthorId { get; set; }
  public string AuthorName { get; set; }
  ...
  public IList<BookDto> Books { get; set; }
}

For Category:

 public partial class Category
 {
    public Category()
    {
        BookCategories = new HashSet<BookCategory>();
    }

    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
    public string Description { get; set; }
    public ICollection<BookCategory> BookCategories { get; set; }
  }

public class CategoryDto
{
  public int CategoryId { get; set; }
  public string CategoryName { get; set; }
  public string Description { get; set; }
  public IList<BookDto> Books { get; set; }
}

And Review :

public partial class Review
{
    public int ReviewId { get; set; }
    public int BookId { get; set; }
    public int UserId { get; set; }
    public DateTime? Date { get; set; }
    public string Comments { get; set; }
    public decimal? Rating { get; set; }
    public Book Book { get; set; }
    public User User { get; set; }
}

public class ReviewDto
{
    public int ReviewId { get; set; }
    public int BookId { get; set; }
    public int UserId { get; set; }
    public DateTime? Date { get; set; }
    public string Comments { get; set; }
    public decimal? Rating { get; set; }
    public Book Book { get; set; }
    public User User { get; set; }
}

I have this :

public IEnumerable<Book> GetAll()
{
  var books = _context.Book
        .Include(e => e.BookAuthors)
        .ThenInclude(a => a.Author)
        .Include(c => c.BookCategories)
        .ThenInclude(categ => categ.Category)
        .Include(r => r.Reviews)
        .AsNoTracking()
        .ToList();
  return books;
}

And then in Author :

public IEnumerable<Author> GetAll()
{
  var authors = _context.Author
        .Include(e => e.BookAuthors)
        .ThenInclude(b => b.Book)
        .ToList();
  return authors;
}

public Author GetById(int id)
{
  return _context.Author.Include("BookAuthors.Book").SingleOrDefault(x => 
  x.AuthorId == id);
}

Between Books and Authors, Books and Categories I have many to many relationship, between Review and Books one to many relationship. I need this because on the list with books I display the name of the author as well, on an author detail page I display his books and so on. I'm using AutoMapper and DTOs as well.

The same for Categories, Reviews..my json with the returned data becomes very big and it takes a lot of time to load the data into the page, because it has this nested structure. What would be the best solution to do this ?

1
1
5/31/2018 10:19:51 AM

Popular Answer

There's a way to do Eager loading. I tried by GroupJoin(expression).SelectMany(...). This will allow you to load till one level avoiding circular rerefence. I'll show you how I archived it, but with your models.

You have:

 var books = _context.Book
    .Include(e => e.BookAuthors)
    .ThenInclude(a => a.Author)
    .Include(c => c.BookCategories)
    .ThenInclude(categ => categ.Category)
    .Include(r => r.Reviews)
    .AsNoTracking()
    .ToList();
 return books;

By the way, you dont put BookAuthors model. So, I'll assume it's structure:

var books = _context.Authors
        .GroupJoin(_context.Book,
                    t1 => new { IdAuthor = (Guid)t1.Id }, //where t1 = Authors, you should have IdAuthor in Book.
                    a => new { IdAuthor = (Guid)a.IdAuthor },     //where a = Book
                    (t1, a_join) => new { t1, a_join })
        .SelectMany(t1 => t1.a_join.DefaultIfEmpty(), (t1, a) => new { t1, a }) //.DefaultIfEmpty() = LEFT JOIN
        .Select(x => new AuthorBooksDTO
        {
            IdAutor = t1.t1.Id //please navegate t1 till VS shoows you which model is
            Books = t1.t1.a_join.toList() //navegate t1. a_join will be the list of books.
            ....
        })
        .ToList();

For sure, it takes more time to build but performance improve incredibly. Let us know if it works for you.

0
6/7/2019 10:16:48 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow