Entity Framework Code First type safe eager loading
In Entity Framework Code First, you can achieve eager loading of
entities by using Include method on DbQuery
in from DbContext
of your application.
In contrary to old Entity Framework, in Code First you cannot use Lambda expression. Only way is to specify property name (or names) by string.
dbContext.MyEntities.Include("EntityRelationship");
This can cause some problems while refactoring solution.
If you don’t mind performance penalty, you can write extension on DbQuery, which will transform lambda expression to string and use it in include. This way you will achieve type safety and better maintainability of code.
Then you can use Lambda expression instead of string.
dbContext.MyEntities.Include((a) => a.EntityRelationship);
First Method extract names of property chain from lambda expression
public static class ExpressionHelper {
public static string GetPropertyName<T>(Expression<Func<T, object>> expression) {
MemberExpression memberExpr = default(MemberExpression);
switch (expression.Body.NodeType) {
case ExpressionType.Convert:
case ExpressionType.AddAssignChecked:
memberExpr = ((UnaryExpression)expression.Body).Operand as MemberExpression;
break;
default:
memberExpr = expression.Body as MemberExpression;
break;
}
if (memberExpr == null)
throw new ArgumentException("Expression must be Member Expression");
if (memberExpr.Expression.NodeType == ExpressionType.Parameter) {
return memberExpr.Member.Name;
}
else {
StringBuilder result = new StringBuilder();
while (memberExpr != null) {
result.Insert(0, '.');
result.Insert(0, memberExpr.Member.Name);
memberExpr = memberExpr.Expression as MemberExpression;
}
result.Remove(result.Length - 1, 1);
return result.ToString();
}
}
}
Second Method creates extension on DbQuery, so it feels natural to apply it
public static class ObjectQueryExtensions {
public DbQuery<T> Include<T>(this DbQuery<T> query, Expression<Func<T, object>> expression) {
string propertyName = ExpressionHelper.GetPropertyName(expression);
return query.Include(propertyName);
}
}
If you look closely on first method, you can notice it already contains some performance optimization. Usual case will consist of just one property include, therefore in that case I skip expensive StringBuilder creation and return property name directly.
I hope you will find this perk useful.