How to perform multiple Linq to Entities orderings dynamically

左心房为你撑大大i 提交于 2019-12-10 15:48:24

问题


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);
            }
        }
    }

回答1:


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];
    IQueryable<DbVersion> dVersions = null;

    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]);
    }
}



回答2:


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

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;
}


来源:https://stackoverflow.com/questions/10549788/how-to-perform-multiple-linq-to-entities-orderings-dynamically

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!