Entity Framework .Include() with compile time checking?

后端 未结 5 1312
误落风尘
误落风尘 2020-12-15 11:11

Consider the following code, which is calling against an EF generated data context:

var context = new DataContext();
var employees = context.Employees.Includ         


        
相关标签:
5条回答
  • 2020-12-15 11:33

    I have used the following with Entity Framework 5. The key is to include System.Data.Entity

    using System.Data.Entity;
    
    context.Customer
        .Include(c => c.Address)
    
    0 讨论(0)
  • 2020-12-15 11:36

    I did a little extension to ObjectQuery which goes like this

    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);
    }
    

    which also requires

    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();
        }
    }
    

    with that you can do

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

    which is going to be check at compile time. If i remember correctly, this methods doesn't work for many-to-many relationship but it works for stuff like

    var item = context.Employees.Include(e => e.Department.Manager);
    

    Good luck to you

    0 讨论(0)
  • 2020-12-15 11:39

    In case you're still using versions prior to Entity Framework 5, the good news is as of C# 6, you can now use nameof to retrieve name of any class/object.

    So now you may do

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

    If you're using EF > 5, then Xavier's answer is better

    0 讨论(0)
  • 2020-12-15 11:40

    Taking moi_meme's idea a step further, my colleague developed the following solution that works in all cases. He introduced a new method caled Includes() for dealing with one-to-many and many-to-many relationships. It allows you to write this:

    context.Customer
        .Include("Address")
        .Include("Orders")
        .Include("Orders.OrderLines")
    

    as this:

    context.Customer
        .Include(c => c.Address)
        .Includes(c => c.Include(customer => customer.Orders)
                        .Include(order => order.OrderLines))
    

    All credit goes to https://stackoverflow.com/users/70427/bojan-resnik, so go give him some love if you like the solution.

    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);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-15 11:50
    var context = new DataContext();
    var employees = context.Employees.Include(context.Department.EntitySet.Name);
    
    0 讨论(0)
提交回复
热议问题