6

When working with IQuerayble<TItem> we can call Select like this:

query.Select( item => new { A=item.Prop1, B=item.Prop2});

And Select method expects Expression<Func<TItem,TResult>>

I need to use ExpandoObject instead of anonymous but statically typed class.

If it were possible it would look like:

query.Select( item => dynamic new ExpandoBoject { A=item.Prop1, B=item.Prop2});

So I want to construct expression tree Expression<Func<TItem,ExpandoObject>> where object's properties are initialized in the similar way as with anonymous type.
Dynamic functionality is only needed for initialization so it's ok that Func returns ExpandoObject instead of dynamic.

I cannot find much documentation about Expression.Dynamic and corresponding binders I should use.


Update 1

Why do I need all this stuff?
Because I want to get primary keys.
I want to do it for any entity type.

I know how to get the list of the properties composing PK, but now I need to make a tricky projection of the entity to EntityKey. Well, may be to same equvalent of this class.

var keys = context.Set<TEntity>().Where(Expression<Func<TEntity,bool>).Select(Expression<Func<TEntity,EntityKey>>);

As I noted in the comments lambdas containing blocks cannot be converted to expression trees so i cannot simple create the dictionary and fill it. Now I'm playing with the expression tree semantically close to this code:

var dict = new Dictionary<string,object>();
dict.Add("Prop1",value1);
dict.Add("Prop2",value2);
return dict

But I doubt EF can parse the expression containing blocks. Need to check.
And I'm curious whether it will work with dynamic objects and Expression.MemberInit as it works with static objects.


Update 2

Entity Framework does not support dictionary initialization syntax.
It throws NotSupportedException with the message: Only list initializer items with a single element are supported in LINQ to Entities.


Update 3

EF does not support block expressions aswell.
NotSupportedException with message: Unknown LINQ expression of type 'Block'.

Community
  • 1
  • 1
Pavel Voronin
  • 13,503
  • 7
  • 71
  • 137
  • You can't do something similar to this: `query.Select( item => dynamic new ExpandoBoject { A=item.Prop1, B=item.Prop2});` Lippert told it: http://stackoverflow.com/questions/7478048/why-cant-i-do-this-dynamic-x-new-expandoobject-foo-12-bar-twelve . – xanatos Aug 21 '13 at 08:07
  • I know, I can't. It was an example of intention to build EXpression tree with dynamics. – Pavel Voronin Aug 21 '13 at 08:13
  • What can be done is create an anonymous type in LINQ and then, after the IQueryable has finished, in the IEnumerable part copy the anonymous type to an Expando object. Clearly both expressions can be autogenerated in some way. – xanatos Aug 21 '13 at 08:15
  • Now keep in mind a bit different apporach. I've implemented my own EntityKey class wich implement IEquatable<> and IDictionary interfaces. For each entity I can generate descendant with properties corresponding to PK whcih whill simply write to the dictionray. But I'm interested whether it is possible to avoid code generation. – Pavel Voronin Aug 21 '13 at 08:22

2 Answers2

4

Now I'm playing with the expression tree semantically close to this code:

var dict = new Dictionary<string,object>();
dict.Add("Prop1",value1);
dict.Add("Prop2",value2);
return dict;

You can do that, because you can write that code as as a single expression like this:

new Dictionary<string, object>
{
    { "Prop1", value1 },
    { "Prop2", value2 }
};

And you can create an expression tree that contains this expression (which EF should be able to handle) like this:

var addMethod = typeof(Dictionary<string, object>).GetMethod("Add");

var expression = Expression.Lambda<Func<Dictionary<string, object>>>(
    Expression.ListInit(
        Expression.New(typeof(Dictionary<string, object>)),
        Expression.ElementInit(
            addMethod,
            Expression.Constant("Prop1"),
            value1Expression),
        Expression.ElementInit(
            addMethod,
            Expression.Constant("Prop2"),
            value2Expression)),
    itemParameterExpression);
svick
  • 236,525
  • 50
  • 385
  • 514
1

The described thing is difficult mainly because we cannot create anonymous type dynamically at run time - they need to be known already at compile time. So my proposition is to create a class that may hold several properties of arbitrary chosen type (similar to Tuple), however we will load from db values only for properties important for us. So we need class like this:

public class CustomTuple<T1, T2>
{
    public T1 Item1 { get; set; }
    public T2 Item2 { get; set; }
}

We may add there more properties if we need more. If we would have such class with 5 properties that with use of it we may load at max 5 properties. Now the projection logic:

Type[] parameterTypes = new Type[] { typeof(int), typeof(object) };
Type tupleType = typeof(CustomTuple<,>).MakeGenericType(parameterTypes);
ParameterExpression x = Expression.Parameter(typeof(Entity));
NewExpression body = Expression.New(tupleType.GetConstructor(new Type[0]), new Expression[0]);
MemberBinding binding1 = Expression.Bind(
    typeof(CustomTuple<,>).MakeGenericType(parameterTypes).GetProperty("Item1"),
    Expression.Property(x, "Value"));
MemberInitExpression memberInitExpression =
    Expression.MemberInit(
        body,
        binding1);

Expression<Func<Entity, object>> exp = Expression.Lambda<Func<Entity, object>>(memberInitExpression, x);
using (MyDbContext context = new MyDbContext())
{
    var list = context.Entities.Select(exp).ToList();
}

The above code selects from Entities collection values of Entity class Value property. parameterTypes defines types of parameters returned from Select projection. If we do not intend to use given property then we leave it as of object type. Then we need to build initialization expression. We do this with method New, we assign the properties values with expression created by Expression.Bind and we combine them together with Expression.MemberInit. We may dynamically create at run time as much MemberBinding expressions as we need.

mr100
  • 4,340
  • 2
  • 26
  • 38
  • Actually, that is similar to what I eventually did. But I started from zero properties. Created analogue of ExpandoObject with allowed inheritance and then dynamically with CodeEmit generated ancestor class with required properties. That allowed me to generically query entities' PKs from EF. – Pavel Voronin Jul 23 '14 at 20:55
  • Good idea to generate those classes dynamically. I will try to edit my answer to include this. – mr100 Jul 23 '14 at 21:05