简体   繁体   English

在LINQ中按字符串值对IQueryable日期列进行排序

[英]Sorting IQueryable date column by string value in LINQ

I am trying to sort an IQueryable object by a specific column via a string input . 我正在尝试通过字符串输入按特定列对IQueryable对象进行排序。

Calling .ToList() on the IQueryable and sorting via a list column works perfectly, however when sorting a date column, it sorts alphabetically, which is not ideal. IQueryable上调用.ToList()并通过列表列进行排序非常有效,但是在对日期列进行排序时,它是按字母顺序排序的,这并不理想。

If anybody could point me in the correct direction here, I'd appreciate it. 如果有人可以在这里指出正确的方向,我将不胜感激。

My Usage 我的用法

IQueryable<MyItemType> list = (from t1 in db.MyTable
                                         select t1);

List<MyItemType> itemsSorted; // Sort here

if (!String.IsNullOrEmpty(OrderBy))
{
    itemsSorted = list.OrderBy(OrderBy).ToList();
}
else
{
    itemsSorted = list.ToList();
}

Extension Method 扩展方式

using System.Linq;
using System.Collections.Generic;
using System;
using System.Linq.Expressions;
using System.Reflection;

public static class OrderByHelper
{
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> enumerable, string orderBy)
{
    return enumerable.AsQueryable().OrderBy(orderBy).AsEnumerable();
}

public static IQueryable<T> OrderBy<T>(this IQueryable<T> collection, string orderBy)
{
    foreach (OrderByInfo orderByInfo in ParseOrderBy(orderBy))
        collection = ApplyOrderBy<T>(collection, orderByInfo);

    return collection;
}

private static IQueryable<T> ApplyOrderBy<T>(IQueryable<T> collection, OrderByInfo orderByInfo)
{
    string[] props = orderByInfo.PropertyName.Split('.');
    Type type = typeof(T);

    ParameterExpression arg = Expression.Parameter(type, "x");
    Expression expr = arg;
    foreach (string prop in props)
    {
        // use reflection (not ComponentModel) to mirror LINQ
        PropertyInfo pi = type.GetProperty(prop);
        expr = Expression.Property(expr, pi);
        type = pi.PropertyType;
    }
    Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
    LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
    string methodName = String.Empty;

    if (!orderByInfo.Initial && collection is IOrderedQueryable<T>)
    {
        if (orderByInfo.Direction == SortDirection.Ascending)
            methodName = "ThenBy";
        else
            methodName = "ThenByDescending";
    }
    else
    {
        if (orderByInfo.Direction == SortDirection.Ascending)
            methodName = "OrderBy";
        else
            methodName = "OrderByDescending";
    }

    //TODO: apply caching to the generic methodsinfos?
    return (IOrderedQueryable<T>)typeof(Queryable).GetMethods().Single(
        method => method.Name == methodName
                && method.IsGenericMethodDefinition
                && method.GetGenericArguments().Length == 2
                && method.GetParameters().Length == 2)
        .MakeGenericMethod(typeof(T), type)
        .Invoke(null, new object[] { collection, lambda });

}

private static IEnumerable<OrderByInfo> ParseOrderBy(string orderBy)
{
    if (String.IsNullOrEmpty(orderBy))
        yield break;

    string[] items = orderBy.Split(',');
    bool initial = true;
    foreach (string item in items)
    {
        string[] pair = item.Trim().Split(' ');

        if (pair.Length > 2)
            throw new ArgumentException(String.Format("Invalid OrderBy string '{0}'. Order By Format: Property, Property2 ASC, Property2 DESC", item));

        string prop = pair[0].Trim();

        if (String.IsNullOrEmpty(prop))
            throw new ArgumentException("Invalid Property. Order By Format: Property, Property2 ASC, Property2 DESC");

        SortDirection dir = SortDirection.Ascending;

        if (pair.Length == 2)
            dir = ("desc".Equals(pair[1].Trim(), StringComparison.OrdinalIgnoreCase) ? SortDirection.Descending : SortDirection.Ascending);

        yield return new OrderByInfo() { PropertyName = prop, Direction = dir, Initial = initial };

        initial = false;
    }

}

private class OrderByInfo
{
    public string PropertyName { get; set; }
    public SortDirection Direction { get; set; }
    public bool Initial { get; set; }
}

private enum SortDirection
{
    Ascending = 0,
    Descending = 1
}
public static IQueryable<T> OrderByIQueryableStringValue<T>(this IQueryable<T> source, string ordering, params object[] values)
{
    var type = typeof(T);
    var property = type.GetProperty(ordering);
    var parameter = Expression.Parameter(type, "p");
    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
    var orderByExp = Expression.Lambda(propertyAccess, parameter);
    MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderBy", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));
    return source.Provider.CreateQuery<T>(resultExp);
} 

}

If you want there is already a library for dynamic linq that has a order by extension method (and others linq methods) that accepts string input for all the data types. 如果需要的话,已经有一个动态linq库,该库具有一个按扩展名排序的方法(和其他linq方法),该库接受所有数据类型的字符串输入。 See http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx 参见http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM