【问题标题】:Paging and sorting based on custom .NET properties with Linq to SQL使用 Linq to SQL 基于自定义 .NET 属性进行分页和排序
【发布时间】:2011-08-16 17:03:51
【问题描述】:

这是this question 关于 ASP.NET 中的自定义分页的后续内容。

假设我定义了一个表:

CREATE TABLE Person
(
  ID int identity(1,1) not null primary key,
  FirstName varchar(50),
  LastName varchar(50)
)

我通过 Linq to SQL 设计器将它映射到一个类。我在Person 类上定义了这个属性:

public string Name
{
  get
  {
    if (FirstName != null && FirstName != "" && LastName != null && LastName != "")
      return String.Format("{0} {1}", FirstName, LastName);
    else if (FirstName == null || FirstName == "")
      return LastName;
    else if (LastName == null || LastName == "")
      return FirstName;
    else
      return "";
  }
}

假设我有一个按绑定到该 Name 属性的列排序的网格,我想做分页。使用 Linq to SQL 执行此操作的自然方式如下:

MyDBContext db = new MyDBContext();
var myData = db.Persons.OrderBy(p => p.Name).Skip(pageSize * pageIndex).Take(pageSize);
myGrid.DataSource = myData;
myGrid.DataBind();

不过,Linq to SQL 会在此处引发错误,因为它can't convert the Name property to SQL。我可以将.AsEnumerable() 放入过滤器字符串中,正如我刚刚引用的问题的答案中所述,但这意味着我将所有行都拉到 Web 服务器并在那里对其进行排序,如果有足够的,这并不理想表中的记录。过滤和排序应该发生在数据库中。

简单的答案是将Name 属性转换为SQL,并使其成为返回数据、视图或其他东西的SPROC 的一部分。但这对我来说感觉不对,因为我将显示逻辑放在我的数据库层中。如果情况比我有点做作的例子更复杂,并且定义的属性更不平凡,那就更糟糕了。

有没有出路,还是我不得不在性能和关注点分离之间做出选择?还是我对数据库中那种显示逻辑的担心是没有根据的?

【问题讨论】:

    标签: c# asp.net linq-to-sql custompaging


    【解决方案1】:

    LINQ to SQL 希望在服务器上运行查询和排序 - 如果表达式可转换为 SQL,它可以执行此操作。

    您的代码无法翻译,这就是您收到错误的原因。

    但是,如果您重构查询以计算名称,它将起作用:

    var query = from p in db.Persons 
                let name = (p.FirstName + " " + p.LastName).Trim
                order by name  
                take 10
                select name;
    

    如果您尝试运行它,您会发现 LINQ-to-SQL 可以很好地为您翻译它:

    SELECT [t2].[value]
            FROM (
                SELECT TOP (10) [t1].[value]
                FROM (
                    SELECT LTRIM(RTRIM(([t0].[FirstName] + @p0) + [t0].[LastName])) AS [value]
                    FROM [dbo].[Persons] AS [t0]
                    ) AS [t1]
                ORDER BY [t1].[value]
                ) AS [t2]
            ORDER BY [t2].[value]
    

    但是,如果表格变大,我建议不要这样做。您要求 SQL 重复执行计算,理想情况下可以执行一次并存储。考虑一个计算字段 FullName,它存储结果。这不需要计算并且可以被索引。

    【讨论】:

    • 这里有两个问题。首先,虽然这个逻辑可以翻译成 SQL,但并不是所有的复杂逻辑都可以;如果逻辑变得比这更复杂,那么它可能无法翻译,你又回到了原点。第二个问题是您的查询产生的 SQL 将给出与他在代码中的逻辑不同的结果。 LTRIMRTRIM 运算符将返回 NULL,如果将 NULL 传递给它,但在这种情况下,代码检查 null 并返回空,您的结果将返回 null。这与所需的输出不同。您应该更正您的代码以产生相同的输出。
    • 计算字段是指我从数据库中选择而不是表中的视图中的内容吗?
    • @Mr. Jefferson:你也不应该在视图中这样做。如果您要重复查询,那么最好将值转换一次,将其存储,然后在需要时查询该值。这里的假设是您的读取次数多于写入次数。
    • 不,并非所有复杂的逻辑都可以翻译成 SQL。如果 SQL 做不到,那么代码必须这样做,并且要对一万条记录进行排序,需要您计算一万次复杂的函数。每次访问它。因此 - 如果你要使用它 - 保存它。
    【解决方案2】:

    我认为您对显示逻辑和数据库分离的担忧是没有根据的,或者更确切地说,我认为Name属性背后的逻辑是显示逻辑的想法是。

    在许多情况下,预先计算的值(即使仅用于显示目的)以帮助查询、显示和优化。

    IMO,您的Persons 表上应该有一个名为Name 的列,并且您的业务层中应该有执行将其他属性转换为Name 属性的逻辑,然后将其保留下来剩下的数据。

    然后,您将能够正常查询,并在数据库级别进行排序(并且您的数据中可能还有其他情况也可以派上用场)。

    仅仅因为分页和排序将结果集过滤到可管理的级别并不意味着重复且经常执行显示逻辑操作是可以接受的。

    另一种思考方式是,您将进行几次转换和编写。但是,您将多次读取此数据,并且每次都将执行此转换。如果您汇总花费在执行此转换上的所有时间,则它会加起来相当多,如果您预先计算该值,然后在需要时对其执行适当的查询操作,您可以节省大量时间。

    【讨论】:

    • 你是否认为UI中网格列绑定的属性应该始终基于存储在DB中的字段,而不是动态计算?排除诸如子元素计数之类的情况。
    • 先生。杰斐逊:对此没有一个明确的答案。是关于使用的。如果您要阅读该列,并且与写入次数相比,它将被写入的次数非常少,那么我绝对说,它应该存储在数据库中。但是,如果该列被多次写入但查询很少,那么我会将其留给显示执行转换。您必须衡量什么对您的系统最有利。但是,鉴于您的示例,您似乎具有后一种情况(读取次数多,写入次数少)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-26
    • 2017-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-29
    • 1970-01-01
    相关资源
    最近更新 更多