3

I am somewhat new to expression trees and I just don't quite understand some things.

What I need to do is send in a list of values and select the columns for an entity from those values. So I would make a call something like this:

DATASTORE<Contact> dst = new DATASTORE<Contact>();//DATASTORE is implemented below.
List<string> lColumns = new List<string>() { "ID", "NAME" };//List of columns
dst.SelectColumns(lColumns);//Selection Command

I want that to be translated into code like this (Contact is an entity using the EF4):

Contact.Select(i => new Contact { ID = i.ID, NAME = i.NAME });

So let's say I have the following code:

public Class<t> DATASTORE where t : EntityObject
{
    public Expression<Func<t, t>> SelectColumns(List<string> columns)
    {
        ParameterExpression i = Expression.Parameter(typeof(t), "i");
        List<MemberBinding> bindings = new List<MemberBinding>();

        foreach (PropertyInfo propinfo in typeof(t).GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            if (columns.Contains(propinfo.Name))
            {
                MemberBinding binding = Expression.Bind(propinfo, Expression.Property(i, propinfo.Name));
                bindings.Add(binding);
            }
        }

        Expression expMemberInit = Expression.MemberInit(Expression.New(typeof(t)), bindings);
        return Expression.Lambda<Func<t, t>>(expMemberInit, i);
    }

When I ran the above code I got the following error:

The entity or complex type 'Contact' cannot be constructed in a LINQ to Entities query.

I looked at the body of the query and it emitted the following code:

{i => new Contact() {ID = i.ID, NAME = i.NAME}}

I am pretty sure that I should be able to construct the a new entity because I wrote this line explicitly as a test to see if this could be done:

.Select(i => new Contact{ ID = i.ID, NAME = i.NAME })

This worked, but I need to construct the select dynamically.

I tried decompiling a straight query(first time I have looked at the low level code) and I can't quite translate it. The high level code that I entered is:

Expression<Func<Contact, Contact>> expression = z => 
                    new Contact { ID = z.ID, NAME = z.NAME };

Changing the framework used in the decompiler I get this code:

ParameterExpression expression2;
Expression<Func<Contact, Contact>> expression = 
   Expression.Lambda<Func<Contact, Contact>>
      (Expression.MemberInit(Expression.New((ConstructorInfo) methodof(Contact..ctor),
         new Expression[0]), new MemberBinding[] { Expression.Bind((MethodInfo) 
            methodof(Contact.set_ID), Expression.Property(expression2 = Expression.Parameter(typeof(Contact), "z"), (MethodInfo) 
            methodof(Contact.get_ID))), Expression.Bind((MethodInfo) 
            methodof(Contact.set_NAME), Expression.Property(expression2, (MethodInfo) 
               methodof(Contact.get_NAME))) }), new ParameterExpression[] { expression2 
        });

I have looked several places to try and understand this but I haven't quite gotten it yet. Can anyone help?

These are some places that I have looked:

Community
  • 1
  • 1
cjohns
  • 1,520
  • 2
  • 14
  • 21

1 Answers1

1

When I did it last time I projected result to not mapped class (not entity) and it worked, everything else was the same as in your code. Are you sure that not dynamic query like .Select(i => new Contact{ ID = i.ID, NAME = i.NAME }) works?

Oleg Mikhailov
  • 252
  • 1
  • 8
  • Hello Oleg, that makes sense but I don't want to create a new class for each Entity because my data store is massive. Also I went back and verified a non dynamic query does select only the columns that I specify. – cjohns Mar 26 '13 at 14:50
  • Tried it too with EF 4 and got NotSupportedException. Maybe it is just as well? If entire entities will be selected they can be safely cached or reused in different places and they will always have actual concurrency token. On the other hand if some entity is too big it can be easily split into main and details entities. – Oleg Mikhailov Mar 27 '13 at 09:35