11
\$\begingroup\$

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:

  1. I can't create an abstract extension class to force IncludeAll() and IncludedFind() method.
  2. I still have to update the extension method if my models change.
  3. I'll have a proliferation of extension methods/classes.
  4. Isn't there an IncludeAll() like method available for Entity Framework?
    • Is there something like this on NuGet?
  5. It just feels wrong...
asked Sep 5, 2013 at 20:36
\$\endgroup\$
3
  • \$\begingroup\$ Could you perhaps use AutoMapper or some other existing mapping service to do what you are wanting? I haven't used AutoMapper but sounds like what you might be looking for. \$\endgroup\$ Commented Sep 5, 2013 at 20:42
  • \$\begingroup\$ Automapper maps models to viewmodels. I don't think it helps loading entities from the database. \$\endgroup\$ Commented Sep 5, 2013 at 20:45
  • 1
    \$\begingroup\$ It doesn't map models to view models. It maps anything to anything from what understand. Just happens that that is a common usage for VM => M and vice versa. But if you have considered and discarded it then no worries. \$\endgroup\$ Commented Sep 6, 2013 at 1:38

3 Answers 3

11
\$\begingroup\$

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.

answered Sep 6, 2013 at 8:19
\$\endgroup\$
3
  • \$\begingroup\$ This would solve my problem, but isn't reflection something you should avoid? As far as I know it's bad for performance and therefore it's condemned by many programmers. I would like to hear your opinion @martijnlentink! \$\endgroup\$ Commented Sep 6, 2013 at 12:01
  • 3
    \$\begingroup\$ Reflection does affect performance, but to be honest I don't think you'll ever experience any issues. Try googling for 'Reflection bad' or 'slow', you will find that most peeps agree that reflection isn't that bad after all. It's just too powerful not to use imho. \$\endgroup\$ Commented Sep 6, 2013 at 13:45
  • \$\begingroup\$ What about the children of children? \$\endgroup\$ Commented May 3, 2018 at 1:33
2
\$\begingroup\$

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());
}
ferada
11.4k25 silver badges65 bronze badges
answered May 6, 2019 at 10:04
\$\endgroup\$
1
  • \$\begingroup\$ Welcome to Code Review, could you rephrase your introduction a bit to make it clearer? Right now the code itself is a better explanation to me than the text. I've also taken the liberty and fixed a few typos, I could only guess at one of the words though, please take a look if I got it wrong. Enjoy your stay! \$\endgroup\$ Commented May 6, 2019 at 23:58
1
\$\begingroup\$

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.

answered Apr 7, 2020 at 15:45
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.