How to OrderBy on a generic IEnumerable (IEnumerable<T>) using LINQ in C#?
Asked Answered
E

6

12

In my generic repository I have below method:

public virtual IEnumerable<T> GetAll<T>() where T : class
{
    using (var ctx = new DataContext())
    {
        var table = ctx.GetTable<T>().ToList();
        return table;
    }
}

T is a Linq to Sql class and I want to be able to OrderBy on a particular property (i.e. int SortOrder). Say if T has property name "SortOrder" then do OrderBy on this property. But I am not sure how I can achieve this. So I need some helps. Thank you! I feel like dynamic languages really shines in doing this kind of jobs!

Quote from ScottGu:

While writing type-safe queries is great for most scenarios, there are cases where you want the flexibility to dynamically construct queries on the fly

And this is exactly the problem I am facing and I am wondering if this linq dynamic helper can be made into official .NET library.

Exmoor answered 5/5, 2010 at 6:5 Comment(3)
You could even overload this method like GetAll<T>(Func<T,bool> orderByClause) and use a expression builder logic for generating the func. Is this something not feasible for your case?Siebert
@Siebert can you please add an answer? Thanks. I think you are referring to something similar to this? blogs.sftsrc.com/stuart/archive/2009/02/19/130.aspxExmoor
Take a look at [this answer][1] Cheers [1]: https://mcmap.net/q/1008445/-problem-with-generic-linq-orderby-functionWhallon
P
4

You could use the dynamic Linq library that would let you construct the OrderBy dynamically: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

Personally I always have my repositories return IQueryable then the consumer can handle building up additional filtering, ordering or projections as needed.

Pyrimidine answered 5/5, 2010 at 13:19 Comment(0)
F
7
public IEnumerable<T> GetAll<T,K>(Expression<Func<T,K>> sortExpr)
{
  using (var ctx = new DataContext())
  {
    ctx.ObjectTrackingEnabled = false;
    var table = ctx.GetTable<T>().OrderBy(sortExpr).ToList();
    return table;
  }
}

Usage:

var t = GetAll<Foo, int>(x => x.Bar);

Unfortunately, you have to supply the type of the key. Unless you start messing with the expressions.

Flood answered 5/5, 2010 at 6:47 Comment(0)
P
5

You can't do it generically, unless you constrain the type parameter T to a type that has the property you want to sort on. For instance, if you have an interface named IHaveTheProperty, then you can do:

public virtual IEnumerable<T> GetAll<T>() where T : class, IHaveTheProperty
{
    using (var ctx = new DataContext())
    {
        ctx.ObjectTrackingEnabled = false;
        var table = ctx.GetTable<T>().ToList().AsReadOnly().OrderBy(t=>t.TheProperty);
        return table;
    }
}

But without a constraint, you can only use the methods of the System.Object type.


LINQ to SQL classes are created partial. That means that you can create another class part to force one of those classes to implement your interface:

public partial class Address : IHaveTheProperty
{
}
Prokofiev answered 5/5, 2010 at 6:8 Comment(3)
This means I need to have all my linq to sql classes that have SortOrder to implement IHaveTheProperty interface?Exmoor
The other issues is how do I pass in a T that implements IHaveTheProperty? Because GetAll is generic and is used across the board so some T types won't have SortOrder property. So it looks like I need to have 2 GetAll methods (1 for with SortOrder T types 1 for without) but this is what I am trying to avoid.Exmoor
Any time a generic method uses a class member, it must be constrained to the set of types that have that member. This can be done by constraining to an interface or by constraining to a base class, but it must be done. If you have some T with and some without a particular member, then you need two separate generic methods with different constraints.Prokofiev
C
5

To get this to work you would need to define a constraint. For example:

public interface IOrderable
{
    int SortOrder { get; }
}

public virtual IEnumerable<T> GetAll<T>() where T : class, IOrderable
{
    ...
}

Now you can use ts.OrderBy(t => t.SortOrder) inside the method body. All the classes you intend to use with this method must then implement this interface.

However as you pointed out LINQ To SQL classes do not implement this interface. I would recommend that you do not take this approach if using LINQ to SQL. With LINQ to SQL it already is very easy to fetch all objects from a table and there is an easy way to order these objects. If you learn to use the provided interfaces correctly your queries will also be much faster and use less memory because you get the database to do filtering for you instead of filtering on the client.

Commonable answered 5/5, 2010 at 6:8 Comment(0)
P
4

You could use the dynamic Linq library that would let you construct the OrderBy dynamically: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

Personally I always have my repositories return IQueryable then the consumer can handle building up additional filtering, ordering or projections as needed.

Pyrimidine answered 5/5, 2010 at 13:19 Comment(0)
S
0

If I was asked to do this I could do something like this:

public virtual IEnumerable<T> GetAll<T>() where T : class
{
    using (var ctx = new DataContext())
    {
        var table = ctx.GetTable<T>().ToList();
        return table;
    }
}

And

publuc virtual IEnumerable<T> GetAll<T>(Func<T,bool> orderByClause) where T:class{

return GetAll<T>().OrderBy(orderByClause);
}

or in .net 4 make the parameter optional and accomplish the same in a single method. You will need a predicate builder like logic to help your client code build delegates on the fly. The link by Jeffrey on my comment kinda illustrates all this stuff! And this link from the extended material from C#3.0 in a nutshell is cool too! If you need to crank the code quick they even have this LinqKit

Siebert answered 5/5, 2010 at 17:4 Comment(0)
M
-1

this should do the trick:

myEnumeration.OrderBy( itm => itm.SortOrder )
Multiplier answered 5/5, 2010 at 6:7 Comment(1)
I don't think this will work as we wouldn't know the type of itm hence no .property would be resolved.Exmoor

© 2022 - 2024 — McMap. All rights reserved.