LINQ如何动态使用多个ThenBy?

4

我没想到这么难找,但似乎是这样。以下是情况说明。

我有一个名为ApplySort的方法,它接收从mvc页面提交的3个参数。

    private List<dynamic> ApplySort(List<dynamic> listToBeSorted, string sortBy, string sortOrder)
    {
        if (String.IsNullOrEmpty(sortBy))
            sortBy = "createddate";
        if (String.IsNullOrEmpty(sortOrder) || sortOrder.Trim() != "0")
            sortOrder = "1"; // 1 = descending, 0 = ascending
        if (sortOrder == "1")
        {
            switch (sortBy)
            {
                case "name":
                    listToBeSorted = listToBeSorted.OrderByDescending(a => a.name).ToList();
                    break;
                case "assigned":
                    listToBeSorted = listToBeSorted.OrderByDescending(a => a.title).ToList();
                    break;
                case "duedate":
                    listToBeSorted = listToBeSorted.OrderByDescending(a => a.end).ToList();
                    break;
                case "status":
                    listToBeSorted = listToBeSorted.OrderByDescending(a => a.title).ToList();
                    break;
                default:
                    listToBeSorted = listToBeSorted.OrderByDescending(a => a.title).ToList();
                    break;
            }
        }
        else
        {
            // same code as in if-block, with just OrderBy calls instead of OrderByDescending
        }
        return listToBeSorted;
    }

两个问题:

1)方法似乎过于冗长(if和else中几乎相同的代码)。

2)我希望能够使用多个列排序。sortBy 参数可以有像“name,title,createddate,status”这样的值。因此应该首先按名称排序,然后按标题排序,然后按创建日期排序......以此类推。我可以通过顺序检查参数来使用ThenBy。 但是如何根据参数值动态应用一系列ThenBy (s),其中ThenBy的数量可能会变化

string[] sortParams = sortBy.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
listToBeSorted.OrderBy(i=>i.sortParams[0]).ThenBy(j=>j.sortParams[1]).ThenBy(k=>k.sortParams[2])...(so on till sortParams.length)

我该如何做这个?另外,我如何在同一行中使用sortOrder参数按升序或降序排序,而不是使用if-else语句。

1
谷歌搜索“Dlinq”,它将允许您执行.ThenBy(new string[]) - Erti-Chris Eelmaa
虽然不完全相同,但是这个答案可能会给你一些启示。它使用枚举而不是字符串来定义搜索属性,但这不应该是一个问题。 - Rawling
1
针对您的情况,似乎拥有一个属性名称和相应lambda表达式的字典可以帮助您消除所有冗余。 - Vikas Gupta
可能是Dynamic LINQ OrderBy on IEnumerable<T>的重复问题。 - gsharp
2个回答

0

您可以创建一个字典,将 Func<string, IComparable> 映射到您的类的属性。

public class ItemWithProperty
{
    public string Property { get; set; }
}

public static void Main()
{
    Dictionary<string, Func<ItemWithProperty, IComparable>> stringPropertyMap = new Dictionary<string, Func<ItemWithProperty, IComparable>>()
    {
       {"param1", item => item.Property}
    };

    List<ItemWithProperty> toBeOrdered = new List<ItemWithProperty>();

    string[] parameters = {"param1"};
    var sorted = toBeOrdered.OrderBy(stringPropertyMap[parameters[0]]);
}

1
但即使如此,我还是需要为sortParams中的每个项目调用stringPropertyMap.Add(),不是吗? - MrClan
你只需要调用一次来将你的类的IComparable属性映射到字符串值。然后,你可以使用你所述的参数链接orderby和thenby。实际上,你应该使用IComparable而不是object——我编辑了我的答案。 - smiech
@MrClan 实际上,在我的代码中,Add() 不是必要的,因为我已经用值初始化了字典。 - smiech

-1

如果您更改了方法签名,可以使用此选项:

    private static IEnumerable<dynamic> ApplySort(IEnumerable<dynamic> listToBeSorted, ICollection<KeyValuePair<string, string>> sorters)
    {
        var orderBy = (sorters == null || sorters.Count == 0) ? new KeyValuePair<string, string>("createddate", "1") : sorters.First();
        var thenBys = (sorters == null || sorters.Count == 1) ? new List<KeyValuePair<string, string>>() : sorters.Except(Enumerable.Repeat(orderBy, 1));

        var orderedEnumerable = orderBy.Value == "1"
            ? listToBeSorted.OrderBy(x => GetPropertyValue(x, orderBy.Key))
            : listToBeSorted.OrderByDescending(x => GetPropertyValue(x, orderBy.Key));

        orderedEnumerable = thenBys.Aggregate(orderedEnumerable, (current, thenBy) => thenBy.Value == "1"
            ? current.ThenBy(x => GetPropertyValue(x, thenBy.Key))
            : current.ThenByDescending(x => GetPropertyValue(x, thenBy.Key)));

        return orderedEnumerable.ToList();
    }

    private static object GetPropertyValue(dynamic obj, string propName)
    {
        Type t = obj.GetType();
        return t.GetProperty(propName).GetValue(obj, null);
    }

尝试:

    static void Main(string[] args)
    {
        var list = new List<dynamic>();
        list.Add(new { name = "Billy" });
        list.Add(new { name = "Johnny" });
        list.Add(new { name = "Ali" });

        var list2 = ApplySort(list, new List<KeyValuePair<string, string>>(new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("name", "1") }));

        foreach (var o in list2)
        {
            Console.WriteLine(o.name);
        }

        Console.ReadLine();

    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接