0

In a C# project, I want to create an extension that would take a list of property names and create a dynamic select query.

I found magic Gist that seems to be doing exactly that. However, var lambda = ... keeps throwing the following error

Lambda type parameter must be derived from System.Delegate

Here is the code

public static IQueryable<dynamic> ToDynamic<T>(this IQueryable<T> query, IEnumerable<String> fields)
{
    var pocoType = typeof(T);

    var itemParam = Expression.Parameter(pocoType, "x");
    var members = fields.Select(f => Expression.PropertyOrField(itemParam, f));
    var addMethod = typeof(IDictionary<string, object>).GetMethod(
                "Add", new Type[] { typeof(string), typeof(object) });


    var elementInits = members.Select(m => Expression.ElementInit(addMethod, Expression.Constant(m.Member.Name), Expression.Convert(m, typeof(object))));

    var expando = Expression.New(typeof(ExpandoObject));
    var lambda = Expression.Lambda<Expression<Func<T, dynamic>>>(Expression.ListInit(expando, elementInits), itemParam);

    return query.Select(lambda.Compile());
}

How can I correct this error?

2

1 Answer 1

1

In Expression.Lambda<T> expression T is considered lambda type parameter that must be derived from System.Delegate. So you just need to remove wrapping Expression and also there is no need to call Compile at the end since IQueryable expect to receive Expression<Func<T, F>> and not a Func<T, F> which is for IEnumerable

//..
var lambda = Expression.Lambda<Func<T, dynamic>>(Expression.ListInit(expando, elementInits), itemParam);

return query.Select(lambda);
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you! Although the var lambda = ... is no longer throwing the previous exception, I am getting the following error ` Only list initializer items with a single element are supported in LINQ to Entities.` when calling the method users.ToDynamic(fields).ToList();
@MikeA A quick fix would be change method to return IEnumerable<dynamic> and change return statement to return query.AsEnumerable().Select(lambda.Compile()); but it means that query won't be executed in database but on client
The whole purpose of doing what I am trying to do is to do the select on the database, not in memory. Is there a way do this on the database server?
@MikeA Well, it's not possible with Dictionary or with ExpandoObject but I can try to investigate a solution with list contains key-value pairs.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.