Dynamically build select list from linq to entities query

前端 未结 3 1702
挽巷
挽巷 2020-12-13 22:21

I\'m looking for a way to dynamically create a select list from a iQueryable object.

Concrete example, i want to do something like the following:

pub         


        
3条回答
  •  悲哀的现实
    2020-12-13 23:05

    Dynamic select expression to a compile time known type can easily be build using Expression.MemberInit method with MemberBindings created using the Expression.Bind method.

    Here is a custom extension method that does that:

    public static class QueryableExtensions
    {
        public static IQueryable Select(this IQueryable source, string[] columns)
        {
            var sourceType = source.ElementType;
            var resultType = typeof(TResult);
            var parameter = Expression.Parameter(sourceType, "e");
            var bindings = columns.Select(column => Expression.Bind(
                resultType.GetProperty(column), Expression.PropertyOrField(parameter, column)));
            var body = Expression.MemberInit(Expression.New(resultType), bindings);
            var selector = Expression.Lambda(body, parameter);
            return source.Provider.CreateQuery(
                Expression.Call(typeof(Queryable), "Select", new Type[] { sourceType, resultType },
                    source.Expression, Expression.Quote(selector)));
        }
    }
    

    The only problem is what is the TResult type. In EF Core you can pass the entity type (like EntityModel.Core.User in your example) and it will work. In EF 6 and earlier, you need a separate non entity type because otherwise you'll get NotSupportedException - The entity or complex type cannot be constructed in a LINQ to Entities query.

    UPDATE: If you want a to get rid of the string columns, I can suggest you replacing the extension method with the following class:

    public class SelectList
    {
        private List members = new List();
        public SelectList Add(Expression> selector)
        {
            var member = ((MemberExpression)selector.Body).Member;
            members.Add(member);
            return this;
        }
        public IQueryable Select(IQueryable source)
        {
            var sourceType = typeof(TSource);
            var resultType = typeof(TResult);
            var parameter = Expression.Parameter(sourceType, "e");
            var bindings = members.Select(member => Expression.Bind(
                resultType.GetProperty(member.Name), Expression.MakeMemberAccess(parameter, member)));
            var body = Expression.MemberInit(Expression.New(resultType), bindings);
            var selector = Expression.Lambda>(body, parameter);
            return source.Select(selector);
        }
    }
    

    with sample usage:

    var selectList = new SelectList();
    selectList.Add(e => e.UserType);
    selectList.Add(e => e.Name);
    
    var selectResult = selectList.Select(entities);
    

提交回复
热议问题