18

This is my query, how can I use string as orderby parameter?

string sortColumn="Title";

var  items = (from ltem in ctxModel.Items
              where ltem.ItemID == vId
              orderby //something here
              select ltem).Skip(PageSize * PageIndex).Take(PageSize);

UPDATE:
I can't just OrderBy the result set, because I FIRST need to sort, and only THEN to page.

verror
  • 349
  • 1
  • 3
  • 9
  • Can you explain what you mean by "use string"? What kind of string? Can you give an example, in normal phrasing, how you want to order this collection? – Grace Note Apr 30 '10 at 19:42
  • @ccornet, It doesn't have to be a string, any parameter that passed to the function and represents desired column to sort by. – verror Apr 30 '10 at 20:47
  • for that that want a full solution to this you can also find at: http://how-to-code-net.blogspot.ro/2014/04/how-to-call-for-dynamic-orderby-method.html Hope it helps you. – Alexa Adrian Apr 11 '14 at 10:09

5 Answers5

14

I use this helper:

public static class OrderExt
{
    private static IOrderedQueryable<T> Order<T>(this IQueryable<T> source, string propertyName, SortDirection descending, bool anotherLevel = false)
    {
        var param = Expression.Parameter(typeof(T), string.Empty);
        var property = Expression.PropertyOrField(param, propertyName);
        var sort = Expression.Lambda(property, param);

        var call = Expression.Call(
            typeof (Queryable),
            (!anotherLevel ? "OrderBy" : "ThenBy") +
            (descending == SortDirection.Descending ? "Descending" : string.Empty),
            new[] {typeof (T), property.Type},
            source.Expression,
            Expression.Quote(sort));

        return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(call);
    }
}

to call the helper, eg do this:

string sort = HttpContext.Current.Request.QueryString["sort"];
var products = _productRepository.OrderBy(sort, SortDirection.Ascending);
Filip Cornelissen
  • 3,682
  • 3
  • 31
  • 41
8

Here's another alternative, the EntitySorter. Allows a bit what dynamic LINQ does with strings, but wraps the operation in a object, just like with the Query Object pattern. It allows both sorting by strings, and by type safe constructs. Here are some examples:

// Ways of defining an entity sorter
// 1. Using strings:
IEntitySorter<Person> sorter = EntitySorter<Person>
    .OrderBy("Address.City")
    .ThenByDescending("Id");

// 2. Defining a sorter with lambda's
IEntitySorter<Person> sorter = EntitySorter<Person>
    .OrderByDescending(p => p.Name)
    .ThenBy(p => p.Id)
    .ThenByDescending(p => p.Address.City);

// 3. Using a LINQ query
IEntitySorter<Person> sorter =
    from person in EntitySorter<Person>.AsQueryable()
    orderby person.Name descending, person.Address.City
    select person;

// And you can pass a sorter from your presentation layer
// to your business layer, and you business layer may look
// like this:
static Person[] GetAllPersons(IEntitySorter<Person> sorter)
{
    using (var db = ContextFactory.CreateContext())
    {
        IOrderedQueryable<Person> sortedList =
            sorter.Sort(db.Persons);

        return sortedList.ToArray();
    }
}

You can find the code here.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • Then go for kervin's answer ;-) – Steven Apr 30 '10 at 21:18
  • 1
    @Verror: The EntitySorter is not a library btw. The implementation consists of 180 LoC and can be copy-pasted into your own project. You can copy-paste it all in once from here: http://servicelayerhelpers.codeplex.com/SourceControl/changeset/view/34401#537056 – Steven May 05 '10 at 19:27
  • In most cases I now prefer using Dynamic Linq as Sky Sanders describes in [his answer](https://stackoverflow.com/a/2748733/264697). – Steven Aug 14 '14 at 08:12
6

Others have suggested using Dynamic link or other libraries. Personally, I would not bring in a library dependency for such a small task. But two other paths that you can take are...

  • Use Object Call syntax and build your query expression tree dynamically. For example...

See http://blog.cincura.net/229310-sorting-in-iqueryable-using-string-as-column-name/

It is important to consider Deferred Execution in this scenario. You can safely build your query that returns an IQueryable object and then run a object query sort on that object. Your query will only be run once, when the data is actually accessed.

The above blog post is an example of how you can use the Expression API to build and expression tree that you can use for your OrderBy. It really just sounds complicated. The MSDN article may be a better reference. See How to: Use Expression Trees to Build Dynamic Queries on MSDN.

Or

  • Use the simple route and just use a switch on the title for the entire query.

Eg.

ItemType items = default(ItemType);
switch(sortColumn)
{
     case "Title":
     {
           items = ctxModel.Items
                    .Where(i => i.ItemID == vId)
                    .OrderBy( i => i.Title);
     }
     break;
 }
kervin
  • 11,672
  • 5
  • 42
  • 59
  • 4
    downvote trigger finger twitching, must resist. This is, IMO, the antithesis of dynamic. wait, it will come to me... oh yeah, this is hardcoded X 2. ;-) – Sky Sanders Apr 30 '10 at 20:22
  • This will require me to make similar cases for all columns, i have 8 of them. – verror Apr 30 '10 at 20:32
  • 3
    @Sky. What are you talking about? – kervin Apr 30 '10 at 21:39
  • 1
    @verror: There are two solutions in my answer. You can (a) Build an expression for your OrderBy dynamically ( see link ), or (b) Use a switch on the column title. I guess "b" doesn't work for you so I don't see another option besides building the expression. – kervin Apr 30 '10 at 21:42
  • using your (a) I should pass my full result set (without paging) through this sorting function and then I should page it as planned? (I need first to sort and then to page) – verror Apr 30 '10 at 22:34
  • 1
    @verror: I've modified my answer with more information, including another article that may help. It's not a result set perse, but more like modifying the expression tree, *before* the result set is returned. Remember that your IQueryable object does not have data yet. – kervin Apr 30 '10 at 23:26
4

Apparently the other allusions to Dynamic Linq are not clear enough. Let me shed some light..

Using Dynamic Linq does not necessarily indicate the need for an assembly dependency.

Dynamic Linq is contained in one source file, if I am not mistaken, that is included in the C# samples everyone should have at least looked at sometime in the last 3 years, and can easily be dropped into a project and namespaced to prevent collisions, thus providing expression building services that can be utilized wherever this need arises.

I consider the ability to safely construct an expression from a reasonably arbitrary string, which can easily be built on the fly to be the best example of 'dynamic'.

Consider:

    var query = northwind.Products
                         .Where("CategoryID = 3 AND UnitPrice > 3")
                         .OrderBy("SupplierID");
Sky Sanders
  • 36,396
  • 8
  • 69
  • 90
  • +1, for no hard feelings. Sky, you should not downvote answers simply because you disagree with them. The dynamic Linq library in the background does exactly what I suggested in the first part of my answer. It builds the criteria expression tree at runtime. MSDN reference also explains the process. The programmer can decide whether he would like to introduce a dependency for that feature or code it himself. – kervin May 01 '10 at 03:48
  • @kervin - did I downvote? In any case, I updated my answer with a clarification of my predisposition towards Dynamic Linq in this case. – Sky Sanders May 01 '10 at 04:35
  • +1 dynamic linq comes in handy for some many other situations...if you have dynamic sorting you will probably need dynamic filtering. – stevebot Nov 21 '13 at 19:32
1

That query looks like you're using custom databinding and/or ObjectDataSource, regardless, there is a way to do this using an extension method which takes a sort expression and dynamically appends an OrderBy() call (expression) to the linq query. I documented how in blog post a while back, which coincidentally was part of this SO question. If you need more than that, you could use dynamic linq which is documented pretty well by scottgu.

EDIT: using the extension method would make it look like

string sortColumn="Title";

    var  items = (from ltem in ctxModel.Items
                  where ltem.ItemID == vId
                  select ltem).Skip(PageSize * PageIndex).Take(PageSize).OrderBy(sortColumn);
Community
  • 1
  • 1
ray2k
  • 99
  • 1
  • 3
  • The problem is that I need first to sort and only then to page – verror Apr 30 '10 at 21:24
  • 1
    If it's a linq query, internally it's just an expression tree. There is no difference between (query).Skip(5).Take(5).OrderBy(p => p.Title); and (query).OrderyBy(p => p.Title).Skip(5).Take(5) But you can call it first if you prefer. – ray2k Apr 30 '10 at 22:21
  • in your example (the one in the answer), OrderBy can't receive strings because the return type of the query is IQueryable – verror May 01 '10 at 08:04
  • Maybe you should follow some of the links then. That 'blog post' link has code for an OrderBy(string) extension method, and the 'dynamic linq' has much more if you need more than just sorting by strings. – ray2k May 01 '10 at 14:34