1

I have a method that calls Entity Framework functions. It is responsible for sending the "select clause", "order by clause" and possible includes (considering that I'm using lazy loading). The method looks like this:

public IEnumerable<TReturn> GetAll<TReturn, TOrderKey>(
    Expression<Func<TEntity, TReturn>> selectExp,
    Expression<Func<TEntity, TOrderKey>> orderbyExp,
    Boolean descending,
    params Expression<Func<TEntity, Object>>[] includeExps)
{
       var query = DbSet.AsQueryable();
       query = !descending ? query.OrderBy(orderByExp) : query.OrderByDescending(orderByExp);
       if (includeExps != null)
          query = includeExps.Aggregate(query, (current, exp) => current.Include(exp));
       return query.Select(selectExp).ToList();
}

When I call:

_service.GetAll(i => new { i.Name}, i => i.Name, false, null);

It works fine! That is the generated SQL is the exactly as I wanted.

However, considering a real scenario (in my case I'm using asp.net mvc), I have an Action method that gets the order parameter from the client.

That is the method:

public JsonResult GetAllUsers(string sortColumn, bool sortDescending)
{
    //sortColumn string must be translated in a Expression
    var users = _service.GetAll(i => new { i.Name, i.Email }, i => i.Name, sortDescending, null);
    //
    //
}

My first attempt was to create an expression for every column, like this:

public JsonResult GetAllUsers(string sortColumn, bool sortDescending)
    {
        //I don't what is the Type that I should put here
        //It can be anything, like: Expression<Func<User, String>>,
        //Expression<Func<User, Guid>>, Expression<Func<User, Int>>
        ?Type? orderExp;
        switch(sortColumn)
        {
             case "UserId":
                 //Expression<Func<User, Guid>> 
                 orderExp = i => i.UserId; 
                 break;
            case "Name":
                 //Expression<Func<User, String>> 
                 orderExp = i => i.Email; 
                 break;
        }
        //sortColumn string must be translated in a Expression
        var users = _service.GetAll(i => new { i.Name, i.Email }, orderExp, sortDescending, null);
        //
        //
    }

I want to create an expression based on sortProperty, there's a lot of information about it over the stackoverflow, however (see the action method) the variable must be typed before the process. The GetAll method can't be called inside every "case" because it returns an anonymous type.

I can't convert all columns to Expression<Func<User, Object>> because entity framework does not support it.

Linq.Dynamic should help, but I don't want to use string parameters.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Fabio
  • 11,892
  • 1
  • 25
  • 41
  • And your question is what exactly? If you cant state your question as a question on your first try, then you will get down voted and closed. – JK. Jun 10 '15 at 22:21
  • 1
    Sorry... I think it was not clear enough. I want to create an expression based on property name, there's a lot of information about it over the stackoverflow, however (see the action method) the variable must be typed before the process. The GetAll returns an anonymous type, and should be called in the end of method. – Fabio Jun 10 '15 at 22:29
  • So you want a predicate builder, except u want to sort and/or select subset of data based on it (have a func, not a predicate). Consider: stackoverflow.com/a/3291811/1144090 - tells us we can't sort by column number which is what I thought to translate the column names into. Or, use [Telerik](http://goo.gl/NmddZ4) :) – h.alex Jun 10 '15 at 23:13
  • I could use a Func, but they are not translated into the SQL. Func<> runs after the data has been load to the memory. – Fabio Jun 10 '15 at 23:26
  • 1
    You can find how to sort by column name here http://stackoverflow.com/a/18321266/1479335. Not strictly what you want, but I guess can be applied at least to sorting. – FireAlkazar Jun 11 '15 at 22:57
  • It helped me a lot. Thanks! I'm still working, if I find a solution I'll post the answer – Fabio Jun 12 '15 at 17:35

1 Answers1

0

You could overload your GetAll method as follows:

public IEnumerable<TReturn> GetAll<TReturn>(
    Expression<Func<TEntity, TReturn>> selectExp,
    string orderColumnName,
    Boolean descending,
    params Expression<Func<TEntity, Object>>[] includeExps)
{
    var entityType = typeof(TEntity);
    var prop = entityType.GetProperty(orderColumnName);
    var param = Expression.Parameter(entityType, "i");
    var orderExp = Expression.Lambda(
        Expression.MakeMemberAccess(param, prop), param);

    // get the original GetAll method overload
    var method = this.GetType().GetMethods().Where(m => m.Name == "GetAll" && m.GetGenericArguments().Length == 2);
    var actualMethod = method.First().MakeGenericMethod(typeof(TReturn), prop.PropertyType);
    return (IEnumerable<TReturn>)actualMethod.Invoke(this, new object[] { selectExp, orderExp, descending, includeExps });
}

Just add a few more checks for null values, otherwise invalid sortColumn names will come back haunting you.

Use it similar to your current approach:

_service.GetAll(i => new { i.Name, i.Email }, sortColumn, sortDescending, null);
grek40
  • 13,113
  • 1
  • 24
  • 50