-1

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.

Community
  • 1
  • 1
Zachary Scott
  • 20,968
  • 35
  • 123
  • 205
  • Can you be a little more specific about what you're trying to accomplish? Like, the context in which you want to use the selector? – John Nelson Oct 31 '10 at 17:43
  • The "anonymous" type is actually the key of the entity. We don't have to be anonymous about that. :) – Zachary Scott Oct 31 '10 at 18:05
  • 2
    using p => "field1,field2,field3" doesn't do any ordering since you order by the string "field1,field2,field3" for every value and thus they're all the same. – Doggett Oct 31 '10 at 18:45
  • You are using underscores on some fields and not on others. You are praising Jon Skeet for a very basic elementary function call. I’m getting the impression that you stitched this together by pasting code from various people and haven’t bothered to do any thinking of your own... – Timwi Nov 01 '10 at 18:09
  • Underscores are private variables. I note others code when I use it. I put questions on SO when I take too long to figure them out on my own. I didn't post that code until Timwi asked to clarify the question. – Zachary Scott Nov 02 '10 at 13:40

3 Answers3

2

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
  • 3,364
  • 21
  • 17
1

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
  • 65,159
  • 33
  • 165
  • 230
0

From your edited question, it appears that you just want to be able to order by multiple keys. That is easily possible simply by using .OrderBy() followed by .ThenBy(). I’m assuming that you are using an IQueryable<E> here:

IQueryable<E> query = ...;
IOrderedQueryable<E> ordered = null;

foreach (var key in entityKeys)
{
    // Code from Doggett to construct the lambda expression for one step
    ParameterExpression param = Expression.Parameter(typeof(E), "a");
    var body = Expression.TypeAs(
        Expression.PropertyOrField(param, key),
        typeof(IComparable));
    var exp = Expression.Lambda<Func<E, IComparable>>(body, param);

    if (ordered == null)
        ordered = query.OrderBy(exp);
    else
        ordered = ordered.ThenBy(exp);
}

var finalQuery = (ordered ?? query).Skip(n).Take(m);
Timwi
  • 65,159
  • 33
  • 165
  • 230