views:

72

answers:

3

I have this:

List<string> fields;

fields[0] = "firstFieldName";
fields[1] = "secondFieldName";
...
fields[n] = "nthFieldName";

I want to get this:

var selector = p => new {p.firstField, p.secondField, ..., p.nthFieldName}

// selector is of type Expression<Func<Entity, object>>

GoofBallLogic had this code that was simliar, ending up with p => p.column

// Entity is an object in a diagram from Entity Framework 4
var p = Expression.Parameter(typeof(Entity, "p");

var selector = Expression.Lambda<Func<Entity, string>(
    Expression.Property(p, columnToGroupBy), p );

EDIT: What I am trying to accomplish

I have a "generic" Repository:

public class Repository<E, C> : IRepository<E,C>
{
    private C _dc {get;set;} // ObjectContext (Entity Framework 4)
    private string _entityName {get;set;}
    public string entityKeyName {get;private set;}
    public List<string> entityKeys {get;private set;}
    public Expression<Func<E, object>> entityKey {get;private set;}
    private EntityContainer _containerName {get;set;}

    public Repository(C myDC)
    {   _dc = myDC; // TODO: check for null

        // Name of "this" ObjectContext
        _containerName = _dc.MetadataWorkspace.GetEntityContainer(
            _dc.DefaultContainerName, DataSpace.CSpace);

        // Name of "this" Entity
        _entityName = _containerName.BaseEntitySets
            .Where(p => p.ElementType.Name == typeof (E).Name)
            .Select( p => p.Name).FirstOrDefault();

        // String list of the keys
        entityKeys = _containerName
            .BaseEntitySets.First(meta => meta.ElementType.Name == 
                typeof(E).Name)
            .ElementType.KeyMembers.Select(k => k.Name).ToList();

        // Thanks Jon Skeet for this cool comma sep list formula
        entityKeyName = string.Join(",", entityKeys.ToArray() );  

        entityKey = Expression.Lambda<Func<E, object>> ... 

What to do to set entityKey as an object that can be used in an OrderBy statement since Linq to Entities requires ordering a set before doing a .Skip().Take()

Edit:

Amazingly, Orderby can take this:

p => "field1,field2,field3"

Which allows my code to execute but doesn't actually order the items by the field values. It's a first step in TDD I guess: use a literal.

+1  A: 

You cannot do this easily because you cannot construct a new expression for a type that doesn’t exist at runtime. (You can have anonymous types in C# because the C# compiler creates the type for you.)

If you want to do it the really hard way, you could generate a dynamic assembly and actually create the type you need. There is a short example here.

I suspect that there is an easier way, though. We would need to know what your goal is (what you need this expression tree for), which you haven’t stated.

Timwi
I will add the code to demonstrate what I am trying to do very shortly. :)
Dr. Zim
Added it after the EDIT:
Dr. Zim
+1  A: 

I found this an interesting problem and took some time to figure it out, and found a relatively easy way to do it.

Anyhow, here's an example on how to do a single field sort (i'll use your first field), if you want to sort on more fields you'll have to create expressions for them too and use .ThenBy(xxx) after the usual OrderBy(xxx).

    // Create a parameter which passes the object
    ParameterExpression param = Expression.Parameter(typeof(E), "a");

    // Create body of lambda expression
    Expression body = Expression.PropertyOrField(param, fieldname);

    // Create lambda function
    Expression<Func<E, string>> exp = Expression.Lambda<Func<E, string>>(body, param);

    // Compile it so we can use it
    Func<E, string> orderFunc = exp.Compile();

Now you can do an OrderBy(orderFunc) and it'll sort the list by the property named in fieldname. Only downside being it only works for string fields (return value of expression). Could probably work around that too though.

Fixed to work with any IComparable type:

        // Create a parameter which passes the field
        ParameterExpression param = Expression.Parameter(typeof(E), "a");

        // Create body of lambda expression
        Expression body = Expression.TypeAs(Expression.PropertyOrField(param, fieldname), typeof(IComparable));

        // Create lambda function
        Expression<Func<E, IComparable>> exp = Expression.Lambda<Func<E, IComparable>>(body, param);

        // Compile it so we can use it
        Func<E, IComparable> orderFunc = exp.Compile();
Doggett
Sweet because I am receiving a comma separated list of OrderBy Fields in the order needed. I will try it right now. :)
Dr. Zim