使用 Linq to SQL 基于自定义 .NET 属性进行分页和排序
这是此问题的后续内容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 在这里抛出错误,因为它 无法将 Name 属性转换为 SQL。我可以将 .AsEnumerable()
放入过滤器字符串中,如我刚刚引用的问题的答案中所述,但这意味着我将所有行拉到 Web 服务器并在那里对它们进行排序,如果表中有足够的记录,这并不理想。过滤和排序应该在数据库中进行。
简单的答案是将 Name
属性转换为 SQL,并使其成为返回数据、视图或其他内容的 SPROC 的一部分。但这对我来说感觉不对,因为我将显示逻辑放在数据库层中。如果情况比我的有些人为的示例更复杂,并且定义的属性更重要,那就更糟糕了。
有没有出路,或者我必须在性能和关注点分离之间做出选择?或者我对数据库中这种显示逻辑的担心是没有根据的?
This is a followup to this question about custom paging in ASP.NET.
Say I have a table defined:
CREATE TABLE Person
(
ID int identity(1,1) not null primary key,
FirstName varchar(50),
LastName varchar(50)
)
And I have it mapped to a class via the Linq to SQL designer. I've defined this property on the Person
class:
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 "";
}
}
Say I've got a grid that is sorted by a column bound to that Name property, and I want to do paging. The natural way to do that with Linq to SQL is something like:
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 throws an error here, though, because it can't convert the Name property to SQL. I can put .AsEnumerable()
in the string of filters, as noted in an answer to the question I just referenced, but that means I'm pulling all rows to the web server and sorting them there, which is not ideal if there are enough records in the table. Filtering and sorting should happen in the database.
The simple answer is to convert that Name
property to SQL and make it part of a SPROC that returns the data, or a view, or something. But that doesn't feel right to me because I'm putting display logic in my database layer. It's even worse if the situation is more complex than my somewhat contrived example, and the property defined is more nontrivial.
Is there a way out, or am I stuck having to choose between performance and separation of concerns? Or is my concern about that kind of display logic in the database unfounded?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
LINQ to SQL 希望在服务器上运行查询和排序 - 如果表达式可转换为 SQL,它就可以执行此操作。
您的代码目前无法翻译,这就是您收到错误的原因。
但是,如果您重构查询来计算名称,它将起作用:
如果您尝试运行它,您会发现 LINQ-to-SQL 可以很好地为您转换它:
但是,如果表变得更大,我建议反对这一点。您要求 SQL 重复执行计算,理想情况下可以执行一次并存储。考虑一个计算字段 FullName,它存储结果。这不需要计算并且可以被索引。
LINQ to SQL wants to run the query and the sort on the server - it can do this if the expression is translateable to SQL.
Your code as it stands isn't translatable, which is why you get the error.
However, if you refactor the query to compute the name, it will work:
If you try running this you find that LINQ-to-SQL does a good job of translating it for you:
However, if the table gets larger I'd recommend against this. You're asking SQL to do a calculation repeatedly that ideally could be done once and stored. Consider a computed field FullName, which stores the result. This would require no calculation and could be indexed.
我认为您对显示逻辑和数据库分离的担忧是没有根据的,或者更确切地说,我认为
Name
属性背后的逻辑是显示逻辑的想法是没有根据的。在许多情况下,即使仅用于显示目的,也会预先计算值以帮助查询、显示和优化。
IMO,您的
Persons
表上应该有一个名为Name
的列,并且您的业务层中应该有执行将其他属性转换为的逻辑。 Name
属性,然后将其与其余数据一起保留。然后,您将能够正常查询,并在数据库级别进行排序(并且在您的数据中可能还有其他情况,这也会派上用场)。
仅仅因为分页和排序将结果集过滤到可管理的水平并不意味着重复且频繁地执行显示逻辑操作是可以接受的。
另一种思考方式是,您将进行几次转换和写入。但是,您将多次读取此数据,并且每次都将执行此转换。如果您汇总执行此转换所花费的所有时间,那么总计起来将是一笔可观的金额,如果您预先计算该值,然后在需要时对其执行适当的查询操作,则可以节省这笔金额。
I think that your concern about the separation of display logic and database is unfounded, or rather, I think that the idea that the logic behind the
Name
property is display logic is.There are many cases where values, even if for display purposes only, are pre-computed to help with querying, display, and optimization.
IMO, you should have a column on your
Persons
table calledName
and you should have the logic in your business layer that performs the transformation of the other properties into theName
property, and then persists it along with the rest of the data.Then, you would be able to query normally, and order on the DB level (and there are probably other cases in your data where this would come in handy as well).
Just because paging and sorting filters the result set down to a manageable level doesn't mean that performing operations for display logic repeatedly and often are acceptable.
Another way to think of it is that you are going to do the transformation and the write a handful of times. However, you are going to read this data many times, and you will be performing this transformation every time. If you aggregate all of the time spent on doing this transformation, it will add up to a significant amount, an amount you could save if you pre-compute the value and then just perform the appropriate query operations on it when you need it.