views:

158

answers:

7

Consider this:

var propertyinfo = typeof(Customer).GetProperty(sortExpressionStr);
Type orderType = propertyinfo.PropertyType;

now i want to declare

Func<int,orderType>

i know its not possible directly since ordertype is at runtime but is there is any workarround ?

this is exactly what i want to do :

        var propertyinfo = typeof(T).GetProperty(sortExpressionStr);
        Type orderType = propertyinfo.PropertyType;

        var param = Expression.Parameter(typeof(T), "x");
        var sortExpression = (Expression.Lambda<Func<T, orderType >>
           (Expression.Convert(Expression.Property(param, sortExpressionStr), typeof(orderType )), param));

all this because i want to convert:

 Expression<Func<T,object>> to Expression<Func<T,orderType>>

or if its not possible then i want to create it from the first place with the right type , the case is as following :

im inside a method which have a type(Customer) and a property name of that type i want to order by it , i want to create a sort expression tree to pass it to Orderby(here)

+4  A: 

You can use the Type.MakeGenericType Method:

Type result = typeof(Func<,>).MakeGenericType(typeof(int), orderType);

This should work:

public static IQueryable<T> OrderByField<T>(
    IQueryable<T> q, string sortfield, bool ascending)
{
    var p = Expression.Parameter(typeof(T), "p");
    var x = Expression.Lambda(Expression.Property(p, sortfield), p);

    return q.Provider.CreateQuery<T>(
               Expression.Call(typeof(Queryable),
                               ascending ? "OrderBy" : "OrderByDescending",
                               new Type[] { q.ElementType, x.Body.Type },
                               q.Expression,
                               x));
}

From here.

dtb
im not sure if this meets my case , and im not sure how to test it , any way i fixed the problem thanks any way dtb for your time and efforts.
Stacker
@Stacker: So, how did you fix the problem? Others might stumble upon your question and be having the same problem.
dtb
i already posted the solution and posted what was wrong in my code . thank u buddy
Stacker
+6  A: 

You can do this by using an open generic type definition, and then making the specific type from that:

typeof(Func<,>).MakeGenericType(typeof(int), orderType);

However, what you're trying to do (calling Lambda<TDelegate>) is not directly possible. You must call Lambda without a type parameter:

var propertyinfo = typeof(T).GetProperty(sortExpressionStr);
Type orderType = propertyinfo.PropertyType;

var param = Expression.Parameter(typeof(T), "x");
var sortExpression = Expression.Lambda(
        Expression.Convert(Expression.Property(param, sortExpressionStr),
                           orderType), 
        param));

This will create the proper Func<,> for you behind the scenes. If you want to compile the expression and use the delegate, you can only do this dynamically with

sortExpression.Compile().DynamicInvoke(param);

If you want to call the OrderBy extension method on Queryable, things get a little more complicated:

var propertyInfo = typeof(T).GetProperty(sortExpressionStr);
Type orderType = propertyInfo.PropertyType;

// first find the OrderBy method with no types specified
MethodInfo method = typeof(Queryable).GetMethods()
  .Where(m => m.Name == "OrderBy" && m.GetParameters().Length == 2)
  .Single();
// then make the right version by supplying the right types
MethodInfo concreteMethod = method.MakeGenericMethod(typeof(T), orderType);

var param = Expression.Parameter(typeof(T), "x");

// the key selector for the OrderBy method
Expression orderBy =
    Expression.Lambda(
        Expression.Property(orderParam, propertyInfo),
        orderParam);

// how to use:
var sequence = new T[0].AsQueryable(); // sample IQueryable

// because no types are known in advance, we need to call Invoke 
// through relection here
IQueryable result = (IQueryable) concreteMethod.Invoke(
                                   null, // = static
                                   new object[] { sequence, orderBy });
Ruben
no buddy , typeof(orderType) wouldnt compile , i tried that already
Stacker
typeof(orderType) is not logical , since your trying to typeof is in compile time and ordertype only in runtime i guess
Stacker
Sorry, `typeof(orderType)` was a copy-paste error. Fixed.
Ruben
Just curious: Why does one need to Expression.Convert the property which has type `orderType` to type `orderType`?
dtb
@dtb when your making dynamic sort and you dont know which property is going to be passed! and what type of it , when its passed to you as a string
Stacker
sadly that wont work too Ruben because then i wont be able to use the return of DynamicInvoke with linqClass.OrderBy(here);
Stacker
cause the return of DynamicInvoke is an object which cant inferred its type.
Stacker
You won't call `DynamicInvoke`, that will happen inside `Enumerable.OrderBy`. But you'll have to call `Enumerable.OrderBy` dynamically as well since you don't know what its generic type arguments are until runtime either.
Ben Voigt
@Stacker: But why do you need to **convert** it? It's already of the right type, no?
dtb
@dtb, i dont need to convert it, i was thinking of that as a possible solution but i need to create it from the first place using the right type but i couldnt, like i said im inside a method which have a type(Customer) and a property name of the time i want to orderby i want to create a sort expression tree to pass it to Orderby(here)
Stacker
+1, I absolutely **love** answers like this one. The code is beautiful and stuff like this often comes in handy when playing with generics via reflection.
gaearon
A: 

You can get the Type associated with Func<int,orderType> in case you wanted to e.g. pass it into CreateDelegate.

But what are you ultimately wanting to do with it? There may be a more straightforward approach.

Ben Voigt
Please check my revision
Stacker
A: 
linqClass.OrderBy(GetSortExpression(sortstr));


public static Expression<Func<T,object>> GetSortExpression<T>(string sortExpressionStr)
    {
        var param = Expression.Parameter(typeof(T), "x");
        var sortExpression = Expression.Lambda<Func<T, object>>(Expression.Property(param, sortExpressionStr), param);
        return sortExpression;
    }

this worked my problem was i used to pass extra parameter Typeof(Object) and orderby used to tell me it cant sort by Object type. thanks all

thanks dtb i will check if your answer work too and i will accept it if it works if not i will accept thsi one.

Stacker
again this code worked !
Stacker
A: 

You want to use Dynamic Linq, a part of the Visual Studio sample code.

Example code using Dynamic Linq

http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

Simon Svensson
yes this would work but i didnt want to include dynamic linq dll
Stacker
It's distributed as a source file, not an assembly. You can compile it into your assembly to avoid any extra references. Take a look in C:\Program Files (x86)\Microsoft Visual Studio 10.0\Samples\1033\CSharpSamples.zip, and the compressed file LinqSamples\DynamicQuery\DynamicQuery\Dynamic.cs
Simon Svensson
A: 

See if my solution dynamic enough.

public class Product
{
    public long ID { get; set; }
    public string Name { get; set; }
    public DateTime Date { get; set; }
}


static void Main(string[] args)
{
    List<Product> products = (from i in Enumerable.Range(1, 10)
                          select new Product { ID = i, Name = "product " + i, Date = DateTime.Now.AddDays(-i) }).ToList();  //the test case

    const string SortBy = "Date";  // to test you can change to "ID"/"Name"

    Type sortType = typeof(Product).GetProperty(SortBy).PropertyType;     // DateTime
    ParameterExpression sortParamExp = Expression.Parameter(typeof(Product), "p");    // {p}
    Expression sortBodyExp = Expression.PropertyOrField(sortParamExp, SortBy);   // {p.DateTime}
    LambdaExpression sortExp = Expression.Lambda(sortBodyExp, sortParamExp);   //   {p=>p.DateTime}
    var OrderByMethod = typeof(Enumerable).GetMethods().Where(m => m.Name.Equals("OrderBy") && m.GetParameters().Count() == 2).FirstOrDefault().MakeGenericMethod(typeof(Product), sortType);
    var result = OrderByMethod.Invoke(products, new object[] { products, sortExp.Compile() });
}

Base on above, it's not difficult to change Product to T to make it generic.

Danny Chen
Yes i could use that if im using linq to enumrable but not linq to sql as this would lead to getting the records the sort so if your onlyb taking first 10 and skiping its going to make trouble as u need to make sort in sql using orderby method while ur retivimg the records !
Stacker
A: 

This seems to be the same question answered here

This approach stays type-safe.

Leon van der Walt