LinqToSQL +分页+动态排序?

时间:2010-03-11 05:57:35

标签: linq linq-to-sql

我在使用LinqToSQL进行分页+动态排序时遇到了问题。这些是我的示例代码。

Using db As New MyDataContext(connectionString)
      db.Log = new DebuggerWritter
      Dim result = db.User.OrderBy(Function(u) u.UserId)

      result = result.Skip((pageNo - 1) * pageSize).Take(pageSize)      
End Using

这是LINQToSQL生成的SQL脚本,它只检索某些记录行。

SELECT [t1].[UserId], [t1].[UserName]
FROM (
    SELECT ROW_NUMBER() OVER (ORDER BY [t0].[UserId]) AS [ROW_NUMBER], [t0].[UserId], [t0].[UserName]
    FROM [dbo].[User] AS [t0]
    ) AS [t1]
WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p0 + @p1
ORDER BY [t1].[ROW_NUMBER]

但如果我实施动态排序,

Using db As New MyDataContext(connectionString)
      db.Log = new DebuggerWritter
      Dim result = db.User

      For Each s In sortExpressions

          Dim expression As Func(Of User, Object) = Function(u) u.[GetType]().GetProperty(s.propertyName).GetValue(u, Nothing)

          Select Case s.SortOrder
               Case SortExpression.SortDirection.Ascending
                    result = result.OrderBy(expression).AsQueryable
               Case SortExpression.SortDirection.Descending
                    result = result.OrderByDescending(expression).AsQueryable
          End Select
      Next

      result = result.Skip((pageNo - 1) * pageSize).Take(pageSize)  
End Using

这是此次生成的SQL脚本,

SELECT [t0].[UserId], [t0].[UserName]
FROM [dbo].[User] AS [t0]

为什么不生成分页控制脚本并且排序已经消失?这是我实现动态排序的方式是错误的吗?或者LinqToSQL不支持分页+动态排序? 帮助!

1 个答案:

答案 0 :(得分:1)

您在代码中执行的操作无效。但是,我对LINQ to SQL没有抛出异常感到有些惊讶。

您在OrderBy中调用的result.OrderBy(expression)方法是Enumerable.OrderBy方法,而不是Queryable.OrderBy方法。您已经注意到了这一点,因为它返回的对象是IEnumerable(Of User)而不是IQueryable(Of User)。因此,您可以通过调用IQueryable(Of User)将其转换为AsQueryable()。但是,调用AsQyeryable无法正常工作。这样做的原因是LINQ to SQL处理表达式树(IQueryable基本上是什么)。表达式树可以动态组合,就像你已经使用result = result.Skip一样。但是,Enumerable不能用于表达式树,而是使用委托,它们是编译的方法调用。虽然您可以将这样的编译方法调用转换为IQueryable,但LINQ to SQL无法对其进行分析,因为它仍然是编译代码(需要自省这样做,只有专门的工具作为Reflector支持)。在这种情况下,LINQ to SQL可能会忽略部分查询的完整顺序,因为它找到了一个无法处理的(编译)方法调用。

因此,您只有在执行以下操作时才能使用解决方案:

Dim expression As Expression(Of Func(Of User, [ExpectedKeyHere]) = _
   ...  creation of the expression here ...

Select Case s.SortOrder
    Case SortExpression.SortDirection.Ascending
        result = result.OrderBy(expression)
    Case SortExpression.SortDirection.Descending
        result = result.OrderByDescending(expression)
End Select
然而,还有另外一件事。你似乎循环了一组sortExpressions。虽然可以附加已排序的集合,但必须使用result.ThenByresult.ThenByDescending调用已排序的集合。用result.OrderBy调用它将完全采用它;你将失去最初的排序顺序。

总而言之,或许您应该尝试构建某种EntitySorter对象,允许您的方法的调用者指定排序顺序。您的服务方法可能看起来像这样(抱歉,它是C#):

public static Person[] GetAllPersons(IEntitySorter<Person> sorter)
{
    Condition.Requires(sorter, "sorter").IsNotNull();

    using (var db = ContextFactory.CreateContext())
    {
        IOrderedQueryable<Person> sortedList =
            sorter.Sort(db.Persons);

        return sortedList.ToArray();
    }
}

I've written a blog about exactly this,再次,它是C#,但我认为它将完全符合您的需求。

祝你好运。