Lets say I have the following entities:
public class EntityA
{
//....
public EntityB Child { get; set; }
}
public class EntityB
{
//....
}
And I have a scoped service where I return these entities:
public class MyScopedService
{
public Task<IQueryable<EntityA>> GetById(int id)
{
var entity = _dbContext.EntityA.First(e => e.Id == id);
return await Task.FromResult(entity);
}
}
Now I want a way to explicitly load the related data, something like this:
public class MyScopedService
{
public async Task<IQueryable<EntityA>> GetById(int id)
{
var entity = _dbContext.EntityA.First(e => e.Id == id);
return await Task.FromResult(entity);
}
public static async Task<EntityA> WithEntityB(this EntityA entity)
{
return await _dbContext.Entry(entity).Reference(e => e.EntityB).LoadAsync();
}
}
So in pages or controllers or wherever, I can do this:
var entity = await _myScopedService.GetById(id).WithEntityB();
Obviously, this will not work as you can't have extension methods inside a non-static class. So what is the best way of achieving something similar inside a scoped service?
I could pass the DbContext as a parameter into the extension method like so:
var entity = await _myScopedService.GetById(id).WithEntityB(_myScopedService.DbContext);
But then I would have to expose my context from inside my scoped service. I am looking for something which does not require this.
Your methods are useless especially if you care about performance.
What you can do additionally - it is just correct GetById
method to return IQueryable
public class MyScopedService
{
public IQueryable<EntityA> GetById(int id)
{
var query = _dbContext.EntityA.Where(e => e.Id == id);
return query;
}
}
WithEntityB
- it is strange replacement of Include
.
var entity = await _myScopedService.GetById(id).Include(e => e.EntityB).FirstAsync();
Anyway, you can add Include
in WithEntityB
and return IQueryable
again:
public static IQueryable<EntityA> WithEntityB(this IQueryable<EntityA> query)
{
return query.Include(e => e.EntityB);
}
var entity = await _myScopedService.GetById(id).WithEntityB().FirstAsync();
Main idea that you have to play with IQueryable
as long as possible. At the end you have to use one of the available materialization methods (First, Single, ToArray, ToList, Count, Any, ect.)
Also, do not create async methods if it is not needed. It is another performance gap.