I am working on a project that is using Repository pattern. We are using Entity Framework to retrieve data from the database. To load related data we use eager loading which means that we have to explicitly include related data.
Say that we want to have all blogs and their related posts we would have to write:
_dbContext.Blogs.Include(b => b.Posts)
The disadvantages with this is that you can only see which related data is loaded in the repository class.
I have tried to shown an example below. Here the problem occurs in the class BlogViewModel.cs
because you try to access blogs.Posts
, but posts is not included in the original query. So if you are trying to get related data, one would have to check the origin of query and see if it is included.
Repository.cs
:
public IEnumerable<BlogDbModel> GetBlogs()
{
return _dbContext.Blogs.ToList();
}
Service.cs
:
public IEnumerable<BlogViewModel> GetBlogs()
{
return _repository.Select(x => new BlogViewModel(x));
}
BlogViewModel.cs
:
public class BlogViewModel
{
public BlogViewModel(BlogDbModel blogDbModel)
{
Name = blogDbModel.Name;
Posts = blogDbModel.Posts;
}
public string Name { get; set; }
public IEnumerable<Posts> Posts { get; set; }
}
Does anyone have a smart solution to this? Is it possible give an compilation error if you are trying to access data that is not included?
Two options that I have thought about are:
IQueryable
from the repositoryThanks
No you don't have any option to give compilation error on this context. Data either way are loaded in the run-time.
Lazy loading
Assuming your displaying Blogs, Posts, Comments in view, So you need to load everything when the page load.
When you enable lazyload
and map properties that not been included in the data layer query it will load related entities
public BlogViewModel(BlogDbModel blogDbModel)
{
Name = blogDbModel.Name;
Posts = blogDbModel.Posts;
}
If you enable lazyload
and do this post will be fetched using lazy load feature. What if post contains a reference to User
or any other object it will load those too.
When you need everything at first place its not recommended to use lazy load because it will additional round-trip to database.
IQueryable
from repository
I would say IQueryable
to a view model is leaky abstraction. Ideally your data layer should be performing these. Providing IQueryable<BlogDbModel>
to a view model is like developer able to fetch 1Blogs=>Post=>User=>Accounts1 anything related from this.
IMHO I would write data layer methods which are needed for the view.
LazyloadingEnabled = false;
Full code:
public IEnumerable<BlogDbModel> GetBlogs()
{
return _dbContext.Blogs.ToList();
}
public IEnumerable<BlogDbModel> GetBlogsAndPosts()
{
return _dbContext.Blogs.Include("Posts").ToList();
}
// This will fetch only what is needed (you can customize to get columns what is needed)
public IEnumerable<NewCustomDTO> GetBlogsAndPostCount()
{
return _dbContext.Blogs.Select(x => NewCustomDTO
{
BlogName = x.BlogName,
Count = x.Posts.Count(),
});
}