代码之家  ›  专栏  ›  技术社区  ›  Mike Chamberlain JaredPar

带有编译时检查的entity framework.include()?

  •  9
  • Mike Chamberlain JaredPar  · 技术社区  · 16 年前

    考虑下面的代码,该代码针对EF生成的数据上下文进行调用:

    var context = new DataContext();
    var employees = context.Employees.Include("Department");
    

    如果我更改部门关系的名称,那么此代码将开始引发运行时错误。那么,是否有任何方法可以安全地调用.include()方法,这样我就可以对所有被引用的关系进行编译时检查?

    5 回复  |  直到 10 年前
        1
  •  8
  •   Xavier John    12 年前

    我在实体框架5中使用了以下内容。关键是要包含System.Data.Entity

    using System.Data.Entity;
    
    context.Customer
        .Include(c => c.Address)
    
        2
  •  7
  •   Community Mohan Dere    9 年前

    我的同事将Moi Meme的想法进一步发展,开发了以下解决方案,在所有情况下都有效。他介绍了一种新方法caled 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);
            }
        }
    }
    
        3
  •  6
  •   moi_meme    16 年前

    我对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);
    

    祝你好运

        4
  •  2
  •   Fabrizio Accatino    14 年前
    var context = new DataContext();
    var employees = context.Employees.Include(context.Department.EntitySet.Name);
    
        5
  •  1
  •   Community Mohan Dere    9 年前

    如果您仍在使用Entity Framework 5之前的版本,那么好消息是从C 6开始,您现在可以使用 nameof 检索任何类/对象的名称。

    所以现在你可以

    var context = new DataContext();
    var employees = context.Employees.Include(nameof(Employees.Department));
    

    如果您使用的是EF>5,则 Xavier's answer is better

    推荐文章