Problem with Generic Linq OrderBy function
Asked Answered
M

5

5

I saw the following function in a posting which allows one to order data using a generic expression:

public static IOrderedQueryable<T> OrderBy<T, TKey>(
  this IQueryable<T> source, Expression<Func<T, TKey>> func, bool isDescending) {
  return isDescending ? source.OrderByDescending(func) : source.OrderBy(func);
}

When I try to use this function I get an error saying "The type or namespace name "TKey' could not be found (are you missing a using directive or an assembly reference?)". I'm doing something dumb here but I can't figure it out.

Edit:

After doing a bit more research, I think my problem is in building the Expression that I pass into it. Is it possible to build an expression that can contain different types? Let's say my dataset has a string, an int, and a bool and I want to use the generic function above to sort by any of the items. How do I do this?

I have this working now:

if (IsString)
{
   Expression<Func<T, string>> expString = ...;
   // call orderBy with expString
}
else if (IsInt)
{
   Expression<Func<T, int>> expInt;
   // call orderBy w/ expInt
}
:

I want something like:

Expression<Func<T, {something generic!}>> exp;
if (IsString)
    exp = ...;
else if (IsInt)
    exp = ...;
:
// call orderBy with exp
Microgroove answered 10/9, 2009 at 19:30 Comment(1)
This seems fine. How are you using this method? Did you add it to a static class?Edmon
M
2

My goal in this was to eliminate a lot of repetitious code. In addition to handling the ascending/descending my "OrderBy" function handles some other common logic has well. Assuming the function definition in the original posting, one can simply do this:

if ( {need to sort by integer})
    query = OrderBy(objectT, a => a.myIntegerField, asc);
else if ( {need to sort by string})
    query = OrderBy(objectT, a=> a.myStringField, asc);
:
Microgroove answered 10/9, 2009 at 21:3 Comment(0)
W
4

One quick observation: You don't really need to use a lambda expression (Expression<Func<T,TKey>>). A simple delegate (Func<T,TKey>) is fine.

That said, I think the answer you might be looking for is this:

Func<T,IComparable> func = null;
if (IsString)
    func = (T a) => a.SomeStringValue;
else if (IsInt)
    func = (T a) => a.SomeIntValue;
// call orderBy with exp
Warrenwarrener answered 14/9, 2009 at 2:0 Comment(4)
This seems like what I was looking. However, I played around with it a little and couldn't get it to compile. When I try to pass func into an OrderBy function the compiler complains. Your method would be slightly cleaner if it would work, but unfortunately I can't devote more time to it right now.Microgroove
When you have time, please tell me what's the compiler error you are seeing.Warrenwarrener
I've finally gotten back to this. I think the problem is that in my custom orderBy function, I make a call to ThenBy which doesn't take a Func argument - it requires the lambda expression.Microgroove
You can create a ThenBy overload that takes a Func then. You should only use lambda expressions when you need to analyze them in some way. If all you need is to execute the expression, then you should use delegates.Warrenwarrener
M
2

My goal in this was to eliminate a lot of repetitious code. In addition to handling the ascending/descending my "OrderBy" function handles some other common logic has well. Assuming the function definition in the original posting, one can simply do this:

if ( {need to sort by integer})
    query = OrderBy(objectT, a => a.myIntegerField, asc);
else if ( {need to sort by string})
    query = OrderBy(objectT, a=> a.myStringField, asc);
:
Microgroove answered 10/9, 2009 at 21:3 Comment(0)
P
1

The expression can only have one type; my preferred answer here would be something like:

IQueryable<T> query = ...
if({case 1}) {
    query = query.OrderBy(x=>x.SomeValue);
} else if({case 2}) {
    query = query.OrderBy(x=>x.SomeOtherValue);
} ...

However, if you want to do something more flexible, you'd probably need to get into custom Expression writing; something more like this.

Piquet answered 10/9, 2009 at 20:34 Comment(1)
That's what I originally had, but I ended up with a ton of "duplicated" code due to the fact I have many fields and need to handle ascending/descending sorts. The answer I posted seems to be working well.Microgroove
C
0

I think what you may be looking for is the ability to have a dynamic orderby clause within linq. For some good articles on the topic see

http://blogs.msdn.com/swiss_dpe_team/archive/2008/06/05/composable-linq-to-sql-query-with-dynamic-orderby.aspx

or

http://www.equivalence.co.uk/archives/819

or

http://www.rocksthoughts.com/blog/archive/2008/01/24/linq-to-sql-dynamic-queries.aspx

Chassepot answered 14/9, 2009 at 9:47 Comment(0)
U
0

Take a look at this answer

My generic handler for sorting is:

  • "dgvProcessList" is my dataGridView
  • "Process" is my object binded to it
  • "e" is my DataGridViewCellMouseEventArgs

                PropertyInfo column = (new Process()).GetType().GetProperties().Where(x => x.Name == dgvProcessList.Columns[e.ColumnIndex].Name).First();
            if (isSortedASC == true)
                dgvProcessList.DataSource = ((List<Process>)dgvProcessList.DataSource).OrderByDescending(x => column.GetValue(x, null)).ToList();
            else
                dgvProcessList.DataSource = ((List<Process>)dgvProcessList.DataSource).OrderBy(x => column.GetValue(x, null)).ToList();
    
            isSortedASC = !isSortedASC;
            dgvProcessList.ClearSelection();
    

Cheers

Unspoken answered 16/10, 2012 at 17:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.