Linq-to-Entities 动态排序

18

这是我的查询,我该如何使用字符串作为orderby参数?

string sortColumn="Title";

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

更新:
我不能只对结果集进行排序,因为我需要首先进行排序,然后才能分页。


你能解释一下“使用字符串”是什么意思吗?是哪种类型的字符串?你能用正常的措辞举个例子,说明你想如何对这个集合进行排序吗? - Grace Note
@ccornet,它不必是一个字符串,任何传递给函数并表示所需排序列的参数都可以。 - verror
如果您需要完整的解决方案,您也可以在以下网址找到:http://how-to-code-net.blogspot.ro/2014/04/how-to-call-for-dynamic-orderby-method.html 希望对您有所帮助。 - Alexa Adrian
5个回答

14

我使用这个工具:

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

调用帮助函数,例如执行以下操作:

string sort = HttpContext.Current.Request.QueryString["sort"];
var products = _productRepository.OrderBy(sort, SortDirection.Ascending);

非常好,这正是我所寻找的 ;) - Alexa Adrian
有没有对相关实体进行排序的解决方案?例如,Department.Name。 - Saboor Awan

8
这里有另一种选择,EntitySorter。它允许使用类似动态LINQ的字符串操作,但将操作封装在对象中,就像查询对象模式一样。它可以通过字符串和类型安全构造进行排序。以下是一些示例:
// 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();
    }
}

您可以在这里找到代码。

那就选择Kervin的答案吧;-) - Steven
1
@Verror:EntitySorter<T>不是一个库。实现包含180行代码,可以复制粘贴到您自己的项目中。您可以从此处一次性复制粘贴所有内容:http://servicelayerhelpers.codeplex.com/SourceControl/changeset/view/34401#537056 - Steven
在大多数情况下,我现在更喜欢使用动态 Linq,就像 Sky Sanders 在他的回答中所描述的那样。 - Steven

6

其他人建议使用动态链接或其他库。 个人而言,我不会为这样一个小任务引入库依赖。 但是你可以采取的另外两条路线是...

  • 使用对象调用语法并动态构建查询表达式树。例如...

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

在这种情况下,考虑到延迟执行非常重要。您可以安全地构建返回IQueryable对象的查询,然后在该对象上运行对象查询排序。只有在实际访问数据时才会运行查询。

上面的博客文章是如何使用Expression API构建和表达式树以供OrderBy使用的示例。它听起来确实很复杂。 MSDN文章可能是更好的参考。请参见MSDN上的如何:使用表达式树构建动态查询

或者

  • 使用简单的路线,只需在整个查询的标题上使用switch。

例如。

ItemType items = default(ItemType);
switch(sortColumn)
{
     case "Title":
     {
           items = ctxModel.Items
                    .Where(i => i.ItemID == vId)
                    .OrderBy( i => i.Title);
     }
     break;
 }

4
按下投票按钮的手指在抽搐,必须忍住。在我看来,这与动态相反。等等,我想起来了...是的,这是硬编码X2。;-) - Sky Sanders
这将需要我为所有列制作类似的情况,我有8个列。 - verror
3
@Sky. 你在说什么? - kervin
1
@verror:我的答案中有两个解决方案。你可以(a)动态构建OrderBy表达式(请参见链接),或者(b)根据列标题使用switch语句。我猜“b”对你来说行不通,所以除了构建表达式之外,我没有看到其他选择。 - kervin
我应该使用您的排序函数来传递完整的结果集(无分页),然后按计划进行分页吗?(首先需要排序,然后再进行分页) - verror
1
@verror:我已经修改了我的答案,添加了更多信息,包括另一篇可能有用的文章。这不是一个结果集合,而更像是在结果集合被返回之前修改表达式树。请记住,你的IQueryable对象还没有数据。 - kervin

4
显然,对于动态Linq的其他暗示不够清晰。让我来解释一下...
使用动态Linq并不一定需要程序集依赖。
如果我没记错的话,动态Linq包含在一个源文件中,该文件包含在每个人都应该至少看过一次的C#示例中,并且可以轻松地添加到项目中并命名空间以避免冲突,从而提供可以在需要时利用的表达式构建服务。
我认为安全地从相当任意的string构建表达式的能力,这可以很容易地实时构建,是“动态”的最好例子。
考虑一下:
    var query = northwind.Products
                         .Where("CategoryID = 3 AND UnitPrice > 3")
                         .OrderBy("SupplierID");

+1,无恶意。Sky,你不应该仅仅因为你不同意答案就给它们投反对票。动态Linq库在后台确切地执行了我在答案的第一部分中建议的操作。它在运行时构建条件表达式树。MSDN参考文献也解释了这个过程。程序员可以决定是否引入依赖于该功能或自己编写代码。 - kervin
@kervin - 我有投反对票吗?无论如何,我已经更新了我的答案,并澄清了我在这种情况下对动态 Linq 的倾向。 - Sky Sanders
+1 动态 LINQ 在许多其他情况下非常方便...如果您有动态排序,您可能需要动态过滤。 - stevebot

1

这个查询看起来像是使用了自定义数据绑定和/或ObjectDataSource。不管怎样,有一种方法可以使用扩展方法来实现这个过程,该方法接受一个排序表达式,并动态地向linq查询追加OrderBy()调用(表达式)。我曾在{{link1:博客文章}}中记录了如何做到这一点,巧合的是这篇博客也是{{link2:这个SO问题}}的一部分。如果你需要更多的帮助,你可以使用dynamic linq,ScottGu已经很好的{{link3:记录}}了它。

编辑:使用扩展方法后看起来就像是这样:

string sortColumn="Title";

    var  items = (from ltem in ctxModel.Items
                  where ltem.ItemID == vId
                  select ltem).Skip(PageSize * PageIndex).Take(PageSize).OrderBy(sortColumn);

问题在于我需要先排序,然后再分页。 - verror
1
如果是Linq查询,它在内部只是一个表达式树。这两个语句(query).Skip(5).Take(5).OrderBy(p => p.Title);和(query).OrderyBy(p => p.Title).Skip(5).Take(5)之间没有区别。但是如果你愿意,你可以先调用它。 - ray2k
在你的示例中(答案中的一个),OrderBy无法接收字符串,因为查询的返回类型是IQueryable。 - verror
也许你应该跟随一些链接。那个“博客文章”链接有一个 OrderBy(string) 扩展方法的代码,而“动态 Linq”则提供了更多选项,如果你需要不仅仅是按字符串排序的话。 - ray2k

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