Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to perform multiple Linq to Entities orderings dynamically

So my problem is like this; in C# code, I need to perform multiple orderings of an entity set with the help of Linq to Entities, dependent on input parameters. There are three columns to order on, and the order of the ordering columns is itself variable (meaning that for each ordering, I need to look up which column to order on). How can I achieve this?

I have some code that should work, but I repeat myself way too much since I haven't been able to parameterize my operations (Linq to Entities is very restrictive wrt. what I'm allowed to do in my lambdas). Please suggest how I can rewrite my code in accordance with the DRY principle, perhaps with the help of T4 code generation?

The following code should illustrate my problem. It's an excerpt of the real code, for brevity, let me know if I should include more. The orderSpecs variable is an array of "order specifications", each of which specifying a column to order on and whether to order in a descending manner. The orderSpecs array has at least one element, so at least one ordering is performed.

using (var db = new MyContainer())
{
    var orderSpec = orderSpecs[0];
    IQueryable<DbVersion> dVersions = null;
    if (orderSpec.Column == 0)
    {
        if (orderSpec.Descending)
        {
            dVersions = db.Versions.OrderByDescending(ver => ver.Name);
        }
        else
        {
            dVersions = db.Versions.OrderBy(ver => ver.Name);
        }
    }
    else if (orderSpec.Column == 1)
    {
        if (orderSpec.Descending)
        {
            dVersions = db.Versions.OrderByDescending(ver => ver.Built);
        }
        else
        {
            dVersions = db.Versions.OrderBy(ver => ver.Built);
        }
    }
    else if (orderSpec.Column == 2)
    {
        if (orderSpec.Descending)
        {
            dVersions = db.Versions.OrderByDescending(ver => ver.Id);
        }
        else
        {
            dVersions = db.Versions.OrderBy(ver => ver.Id);
        }
    }

    foreach (var spec in orderSpecs.Skip(1))
    {
        if (spec.Column == 0)
        {
            if (spec.Descending)
            {
                dVersions = dVersions.ThenByDescending(ver => ver.Name);
            }
            else
            {
                dVersions = dVersions.ThenBy(ver => ver.Name);
            }
        }
        else if (spec.Column == 1)
        {
            if (spec.Descending)
            {
                dVersions = dVersions.ThenByDescending(ver => ver.Built);
            }
            else
            {
                dVersions = dVersions.ThenBy(ver => ver.Built);
            }
        }
        else if (spec.Column == 2)
        {
            if (spec.Descending)
            {
                dVersions = dVersions.ThenByDescending(ver => ver.Id);
            }
            else
            {
                dVersions = dVersions.ThenBy(ver => ver.Id);
            }
        }
    }
like image 289
aknuds1 Avatar asked May 11 '12 10:05

aknuds1


2 Answers

What about creating a dictionary for mapping these colums that are causing these huge if-else constructs to the properties. Could look like this:

using (var db = new MyContainer())
{
    var orderSpec = orderSpecs[0];
    IOrderedEnumerable<DbVersion> dVersions;

    var mapping = new Dictionary<int, Func<DbVersion, object>>()
    {
        { 0, ver => ver.Name },
        { 1, ver => ver.Built },
        { 2, ver => ver.Id }
    };

    if (orderSpec.Descending)
        dVersions = db.Versions.OrderByDescending(mapping[orderSpec.Column]);
    else
        dVersions = db.Versions.OrderBy(mapping[orderSpec.Column]);

    foreach (var spec in orderSpecs.Skip(1))
    {
        if (spec.Descending)
            dVersions = dVersions.ThenByDescending(mapping[spec.Column]);
        else
            dVersions = dVersions.ThenBy(mapping[spec.Column]);
    }
}
like image 70
Philip Daubmeier Avatar answered Sep 27 '22 22:09

Philip Daubmeier


For Untype : You can also make use of Dynamic Linq Library : Using the LINQ Dynamic Query Library

enter image description here

OR

Full article : Handle GridView.OnSorting() and create sorting expression dynamically using LINQ

For Typed : You can do dynamic sorting as below which remove the code that you have written

How to do soring on class called person where the columnname and sorting direction is not fix

IEnumerable<Person> persons = GetPersons();
    persons = persons.OrderBy(e.SortExpression, e.SortDirection);

Person class

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Generic method with expression tree for sorting data

public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> collection, 
       string columnName, SortDirection direction)
{
    ParameterExpression param = Expression.Parameter(typeof(T), "x"); // x
    Expression property = Expression.Property(param, columnName);     // x.ColumnName
    Func<T, object> func = Expression.Lambda<Func<T, object>>(        // x => x.ColumnName
        Expression.Convert(Expression.Property(param, columnName), 
        typeof(object)), param).Compile();

    Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> expression =
        SortExpressionBuilder<T>.CreateExpression(direction);
    IEnumerable<T> sorted = expression(collection, func);
    return sorted;
}
like image 29
Pranay Rana Avatar answered Sep 27 '22 22:09

Pranay Rana