As commonly known in EF-Core there is no Lazy loading. So that kind of means I'm forced to do my queries with some afterthought. So since I have to think, then i might as well try to do it properly.
I have a Fairly standard update query, but I thought hey, I don't always have to include the HeaderImage
and PromoImage
FK-objects. There should be a way to make that happen. But I can just not find a way to perform a Include at a later point. In-fact I would like to maybe include right before I actually do work on the object. That way i might be able to automate some of the eagerness.
ArticleContent ac = _ctx.ArticleContents
.Include(a=> a.Metadata)
.Include(a=> a.HeaderImage)
.Include(a=> a.PromoImage)
.Single(a => a.Id == model.BaseID);
ac.Title = model.Title;
ac.Ingress = model.Ingress;
ac.Body = model.Body;
ac.Footer = model.Footer;
if (model.HeaderImage != null)
{
ac.HeaderImage.FileURL = await StoreImage(model.HeaderImage, $"Header_{model.Title.Replace(" ", "_")}_{rand.Next()}");
}
if (model.PromoImage != null)
{
ac.PromoImage.FileURL = await StoreImage(model.PromoImage, $"Promo_{model.Title.Replace(" ", "_")}_{rand.Next()}");
}
ac.Metadata.EditedById = uId;
ac.Metadata.LastChangedTimestamp = DateTime.Now;
await _ctx.SaveChangesAsync();
EXTRA
To be clear, this is EF7 (Core), and im after a solution that allows me to add includes on demand, hopefully after the initial _ctx.ArticleContents.Include(a=> a.Metadata).Single(a => a.Id == model.BaseID).
I'm using something similar to Alexander Derck's solution. (Regarding the exception mentioned in the comments: ctx.ArticleContents.AsQueryable() should also work.)
For a couple of CRUD MVC sites I'm using a BaseAdminController
. In the derived concrete Controllers I can add Includes dynamically. From the BaseAdminController:
// TModel: e.g. ArticleContent
private List<Expression<Func<TModel, object>>> includeIndexExpressionList = new List<Expression<Func<TModel, object>>>();
protected void AddIncludes(Expression<Func<TModel, object>> includeExpression)
{
includeIndexExpressionList.Add(includeExpression);
}
Later I saw that I need more flexibility, so I added a queryable. E.g. for ThenInclude().
private Func<IQueryable<TModel>, IQueryable<TModel>> IndexAdditionalQuery { get; set; }
protected void SetAdditionalQuery(Func<IQueryable<TModel>, IQueryable<TModel>> queryable)
{
IndexAdditionalQuery = queryable;
}
Here the Index action:
public virtual async Task<IActionResult> Index()
{
// dynamic include:
// dbset is for instance ctx.ArticleContents
var queryable = includeIndexExpressionList
.Aggregate(dbSet.AsQueryable(), (current, include) => current.Include(include));
if(IndexAdditionalQuery != null) queryable = IndexAdditionalQuery(queryable);
var list = await queryable.Take(100).AsNoTracking().ToListAsync();
var viewModelList = list.Map<IList<TModel>, IList<TViewModel>>();
return View(viewModelList);
}
In the concrete Controller I use:
AddIncludes(e => e.EventCategory);
SetAdditionalQuery(q => q
.Include(e => e.Event2Locations)
.ThenInclude(j => j.Location));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With