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.