考虑以下代码,该代码针对EF生成的数据上下文进行调用:
var context = new DataContext();
var employees = context.Employees.Include("Department");
如果我更改部门关系的名称,那么此代码将开始抛出运行时错误。那么有没有办法以安全的方式调用.Include()方法,所以我得到编译时检查所引用的所有关系?
答案 0 :(得分:8)
我在Entity Framework 5中使用了以下内容。关键是要包含System.Data.Entity
using System.Data.Entity;
context.Customer
.Include(c => c.Address)
答案 1 :(得分:7)
将moi_meme的想法更进一步,我的同事开发了以下适用于所有情况的解决方案。他介绍了一种新方法Includes()
,用于处理一对多和多对多关系。它允许你写这个:
context.Customer
.Include("Address")
.Include("Orders")
.Include("Orders.OrderLines")
这样:
context.Customer
.Include(c => c.Address)
.Includes(c => c.Include(customer => customer.Orders)
.Include(order => order.OrderLines))
所有功劳归于https://stackoverflow.com/users/70427/bojan-resnik,所以如果你喜欢这个解决方案,请给他一些爱。
public static class ObjectQueryExtensions
{
public static ObjectQuery<T> Includes<T>(this ObjectQuery<T> query, Action<IncludeObjectQuery<T, T>> action)
{
var sb = new StringBuilder();
var queryBuilder = new IncludeObjectQuery<T, T>(query, sb);
action(queryBuilder);
return queryBuilder.Query;
}
public static ObjectQuery<TEntity> Include<TEntity, TProperty>(this ObjectQuery<TEntity> query, Expression<Func<TEntity, TProperty>> expression)
{
var sb = new StringBuilder();
return IncludeAllLevels(expression, sb, query);
}
static ObjectQuery<TQuery> IncludeAllLevels<TEntity, TProperty, TQuery>(Expression<Func<TEntity, TProperty>> expression, StringBuilder sb, ObjectQuery<TQuery> query)
{
foreach (var name in expression.GetPropertyLevels())
{
sb.Append(name);
query = query.Include(sb.ToString());
Debug.WriteLine(string.Format("Include(\"{0}\")", sb));
sb.Append('.');
}
return query;
}
static IEnumerable<string> GetPropertyLevels<TClass, TProperty>(this Expression<Func<TClass, TProperty>> expression)
{
var namesInReverse = new List<string>();
var unaryExpression = expression as UnaryExpression;
var body = unaryExpression != null ? unaryExpression.Operand : expression.Body;
while (body != null)
{
var memberExpression = body as MemberExpression;
if (memberExpression == null)
break;
namesInReverse.Add(memberExpression.Member.Name);
body = memberExpression.Expression;
}
namesInReverse.Reverse();
return namesInReverse;
}
public class IncludeObjectQuery<TQuery, T>
{
readonly StringBuilder _pathBuilder;
public ObjectQuery<TQuery> Query { get; private set; }
public IncludeObjectQuery(ObjectQuery<TQuery> query, StringBuilder builder)
{
_pathBuilder = builder;
Query = query;
}
public IncludeObjectQuery<TQuery, U> Include<U>(Expression<Func<T, U>> expression)
{
Query = ObjectQueryExtensions.IncludeAllLevels(expression, _pathBuilder, Query);
return new IncludeObjectQuery<TQuery, U>(Query, _pathBuilder);
}
public IncludeObjectQuery<TQuery, U> Include<U>(Expression<Func<T, EntityCollection<U>>> expression) where U : class
{
Query = ObjectQueryExtensions.IncludeAllLevels(expression, _pathBuilder, Query);
return new IncludeObjectQuery<TQuery, U>(Query, _pathBuilder);
}
}
}
答案 2 :(得分:6)
我对ObjectQuery做了一点扩展,就像这样
public static ObjectQuery<TEntity> Include<TEntity, TProperty>(this ObjectQuery<TEntity> query, Expression<Func<TEntity, TProperty>> expression) where TEntity : class
{
string name = expression.GetPropertyName();
return query.Include(name);
}
也需要
public static class ExpressionExtensions
{
public static string GetPropertyName<TObject, TProperty>(this Expression<Func<TObject, TProperty>> expression) where TObject : class
{
if (expression.Body.NodeType == ExpressionType.Call)
{
MethodCallExpression methodCallExpression = (MethodCallExpression)expression.Body;
string name = ExpressionExtensions.GetPropertyName(methodCallExpression);
return name.Substring(expression.Parameters[0].Name.Length + 1);
}
return expression.Body.ToString().Substring(expression.Parameters[0].Name.Length + 1);
}
private static string GetPropertyName(MethodCallExpression expression)
{
MethodCallExpression methodCallExpression = expression.Object as MethodCallExpression;
if (methodCallExpression != null)
{
return GetPropertyName(methodCallExpression);
}
return expression.Object.ToString();
}
}
你可以做到
var context = new DataContext();
var employees = context.Employees.Include(e => e.Department);
将在编译时检查。如果我没记错的话,这种方法对于多对多关系不起作用,但它适用于像
这样的东西var item = context.Employees.Include(e => e.Department.Manager);
祝你好运
答案 3 :(得分:2)
var context = new DataContext();
var employees = context.Employees.Include(context.Department.EntitySet.Name);
答案 4 :(得分:1)
如果您仍在使用Entity Framework 5之前的版本,那么好消息是从C#6开始,您现在可以使用var context = new DataContext();
var employees = context.Employees.Include(nameof(Employees.Department));
来检索任何类/对象的名称。
所以现在你可以做到
{{1}}
如果你正在使用EF&gt; 5,然后Xavier's answer is better