Problem
The Problem
As an ASP.NET MVC4 developper, I’m using Entity Framework a lot. Considering performance I often use lazy loading for my models.
public class Result {
public int Id { get; set; }
public decimal Grade { get; set; }
public virtual Skill Skill { get; set; }
public int SkillId { get; set; }
public virtual Player Player { get; set; }
public int PlayerId { get; set; }
public virtual Category { get; set; }
public int CategoryId { get; set; }
}
If I want to include all navigation models I’ll have to include all those models.
public ActionResult Details(int id = 0)
{
Result result = db.Results
.Include(r => r.Skill)
.Include(r => r.Player)
.Include(r => r.Category)
.SingleOrDefault(r => r.Id == id);
//some viewmodel mapping
return View(viewmodel);
}
My solution
I built an extension method to remove this from my controller code.
public static class IncludeExtensions
{
public static IQueryable<Result> IncludeAll(this IQueryable<Result> results)
{
return results.Include(r => r.Skill)
.Include(r => r.Player)
.Include(r => r.Category);
}
public static Result IncludedFind(this IQueryable<Result> results, int id)
{
return results.IncludeAll().SingleOrDefault(r => r.Id == id);
}
}
public ActionResult Details(int id = 0)
{
Result result = db.Results.IncludedFind(id);
//some viewmodel mapping
return View(viewmodel);
}
There are a few problems with this:
- I can’t create an abstract extension class to force
IncludeAll()
andIncludedFind()
method. - I still have to update the extension method if my models change.
- I’ll have a proliferation of extension methods/classes.
- Isn’t there an
IncludeAll()
like method available for Entity Framework?- Is there something like this on NuGet?
- It just feels wrong…
Solution
A simple option would be to use reflection to check for properties that are virtual and has the Id-suffix. This is where I came up with, working for me;
public static IQueryable<T> IncludeAll<T>(this IQueryable<T> queryable) where T : class
{
var type = typeof (T);
var properties = type.GetProperties();
foreach (var property in properties)
{
var isVirtual = property.GetGetMethod().IsVirtual;
if (isVirtual && properties.FirstOrDefault(c => c.Name == property.Name + "Id") != null)
{
queryable = queryable.Include(property.Name);
}
}
return queryable;
}
I hope this answers your question.
Include uses when you want to include ICollection
properties or not int
, string
, bool
, etc. types. I use this in my base repository that precedes all my entities.
public IQueryable<T> GetIncludes(IQueryable<T> Queryable)
{
var normal_types = new List<Type>() {
typeof(int),
typeof(string),
typeof(bool)
};
var ty = typeof(T);
foreach (var item in ty.GetProperties())
{
if (!normal_types.Contains(item.GetType()))
{
Queryable.Include(item.Name);
}
}
return Queryable;
}
public IQueryable<T> GetIncludes(DbSet<T> Queryable)
{
return GetIncludes(Queryable.AsQueryable());
}
EF knows about all navigation properties. So, instead of using reflection and try to figure it out by myself, I can just ask the DbContext
about navigation properties.
Here‘s a wonderful SO answer that does exactly that.