Reputation: 4045
I am using Entity Framework Core following Chris Sakell's blog here.
He uses generics to manage his repositories and also a base repository that he uses for all the other repositories.
Part of the base repository has the the following code for the retrieval of a single entity that also downloads related entities using the includeProperties
option. Here is the generic code for a retrieving a single item.
public T GetSingle(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> query = _context.Set<T>();
foreach (var includeProperty in includeProperties)
{
query = query.Include(includeProperty);
}
return query.Where(predicate).FirstOrDefault();
}
I am using it on a client table that has many jobs attached to it.
This is how I structured my code.
public ClientDetailsViewModel GetClientDetails(int id)
{
Client _client = _clientRepository
.GetSingle(c => c.Id == id, c => c.Creator, c => c.Jobs, c => c.State);
if(_client != null)
{
ClientDetailsViewModel _clientDetailsVM = mapClientDetailsToVM(_client);
return _clientDetailsVM;
}
else
{
return null;
}
}
The line:
.GetSingle(c => c.Id == id, c => c.Creator, c => c.Jobs, c => c.State);
successfully retrieves values for creator state and job.
However, nothing is retrieved for those related entities associated with the "jobs".
In particuar, JobVisits
is a collection of visits to jobs.
For completeness I am adding the "job" and "jobvisit" entities below
public class Job : IEntityBase
{
public int Id { get; set; }
public int? ClientId { get; set; }
public Client Client { get; set; }
public int? JobVisitId { get; set; }
public ICollection<JobVisit> JobVisits { get; set; }
public int? JobTypeId { get; set; }
public JobType JobType { get; set; }
public int? WarrantyStatusId { get; set; }
public WarrantyStatus WarrantyStatus { get; set; }
public int? StatusId { get; set; }
public Status Status { get; set; }
public int? BrandId { get; set; }
public Brand Brand { get; set; }
public int CreatorId { get; set; }
public User Creator { get; set; }
....
}
public class JobVisit : IEntityBase
{
...
public int? JobId { get; set; }
public Job Job { get; set; }
public int? JobVisitTypeId { get; set; }
public JobVisitType VisitType { get; set; }
}
My question is, how do I modify the repository code above and my GetSingle
use so that I can also load the related enitities JobVisit
collection and the other related single entities Brand
and JobType
?
Upvotes: 2
Views: 135
Reputation: 1545
It is intended that navigation properties are not necessary retrieved for associated with the "jobs". That is why some properties are null
. By default the .Include(property);
goes only 1-level deep and that is a good thing. It prevents your query from fetching all the data of your database.
If you want to include multiple levels, you should use .ThenInclude(property)
after .Include(property)
. From the documentation:
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ToList();
}
My advice is that your method public T GetSingle(...)
is nice and I would not change it in order to include deeper levels. Instead of that, you can simply use explicit loading. From the documentation:
using (var context = new BloggingContext())
{
var blog = context.Blogs
.Single(b => b.BlogId == 1);
context.Entry(blog)
.Collection(b => b.Posts)
.Load();
context.Entry(blog)
.Reference(b => b.Owner)
.Load();
}
Upvotes: 1