简体   繁体   中英

Loading nested properties in Entity Framework Core

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 ?

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM