LINQ to SQL系列Part 3 - Querying our Database

来源:互联网 发布:知乎 snh48直弯 编辑:程序博客网 时间:2024/05/16 23:35

LINQ to SQL系列Part 3 - Querying our Database
本文转载自:
http://www.cnblogs.com/hanxianlong/archive/2007/11/18/962692.html

英文原贴链接:
http://weblogs.asp.net/scottgu/archive/2007/06/29/linq-to-sql-part-3-querying-our-database.aspx
 

上个月我开始了一个讲解LINQ to SQL的帖子系列。LINQ to SQL是集成在.NET Framework3.5中的O/RM(对象关系映射)的实现,它让你非常容易地用.NET类来生成关系型数据库的模型。然后你可以用LINQ 表达式对它来进行查询,更新,插入删除。

在今天的帖子中,我将会更详细地讲解一下如何使用我们在第2部分(Part 2 )中生成数据模型,并且讲解一下如何在ASP.NET项目中使用它来查询数据。

用LINQ to SQL生成的Northwind数据库

在本系列的第2部分( Part 2)中,我讲解了如何用VS2008中的LINQ to SQL设计器来生成LINQ to SQL类模型。下面是从Northwind事例数据库生成的类模型:         
 

查询产品

一旦我们定义了如上的数据模型类,我们就可以非常方便地从数据库中查询并且检索数据。LINQ to SQL允许你通过对上边的用LINQ to SQL设计器生成的NorthwindDataContext类写LINQ语句来对数据库进行查询和检索。

例如,我可以通过写如下的代码来查过和对产品对象序列进行循环查询:

 


在上边的查询中,我在LINQ 语句中用"where"语句来限定仅返回属于某种特定类型的产品。这里我是用Product的CategoryID作为过滤条件。

上边的LINQ to SQL的其中一个比较好的地方是,在如何查询数据这个问题上有很大的灵活性,并且我可以用已经先定义在LINQ to SQL 数据类中的关联来对数据库进行更丰富和更自然的查询。例如,我可以像如下这样更改我的代码以实现对产品用类别名而不是用类别ID进行查询:


注意上边我是如何用每个Product对象的”Category"属性来来对产品对象列表通过类别名称进行过滤。这个属性是LINQ to SQL自动为我们生成的,因为我们将数据库的Category和Product表有一对多的关系也生成在了Category和Product类之间了。

下面是另外一个简单的在查询中使用生成的关联关系进行查询的小例子,我可以通过写如下的LINQ查询语句来查出超过5条订单详细信息的产品列表:

 
 

注意我们用的"OrderDetails"集合,它是LINQ to SQL已经为我们在每个Product类上生成好的(因为我们用LINQ to SQL设计器生成的有一对多的关系)。

在调试状态下查看LINQ to SQL查询

当你对像LINQ to SQL这样的对象关系映射生成的对象模型进行查询或者更新时,它们会自动地处理生成和执行适合的SQL语句。

和开发者对ORMs关联并且困扰着他们的是“它实际上执行的是什么SQL语句?“,LINQ to SQL真正好的一处是它可以在调试状态下运行程序时,很方便地看它执行了什么SQL代码。

用VS2008Beta2作为开始,你可以用一个新的LINQ to SQL可视化插件来方便地查询(测试出)任何的LINQ to SQL查询表达式。简单地设计一个断点,然后在调试状态下将鼠标放到LINQ to SQL查询,点击放大器来将查看查询表达式。

 

 然后,这个操作就会弹出一个窗口,该窗口中展示的就是你在用该LINQ to SQL检索产品对象时用到的完整的SQL语句:

 

 如果在该对话框中你点击“Execute”,它会直接在调试器中执行SQL语句并且能够查询从数据库中返回的数据:

很明显,这使得你查看LINQ to SQL为你生成的精确的SQL查询逻辑变得非常地方便。注意,如果想改变它,你也可以选择重写LINQ to SQL为你生成的SQL语句--虽然在98%的情况下我想你会发现LINQ to SQL执行的SQL语句非常非常地好。

将LINQ to SQL的查询和ASP.NET 的控件绑定

LINQ 查询返回一个实现了IEnumerable接口的集合--IEnumerable也是支持数据绑定的ASP.NET服务器控件实现的接口。说这个的意思就是你可以将任何的LINQ,LINQ to SQL或LINQ to XML查询的结果集绑定到任何的ASP.NET控件。

例如,我可以在.aspx页面像这样声明一个<asp:gridview>控件:
 
 

然后我可以像下面这样写LINQ to SQL的查询来绑定查询结果到gridview上:

 

然后它就就会生成一个如下的页面:

格式化查询结果

下面,当我们想评估我们的产品查询,默认情况下我们查出所有的在Product实体类中存在的列。例如用下面这个查询来检索产品:

   

返回的所有数据结果集:

通常我们只想返回每个产品的所有属性集合的一个子集。这样的话,我们就可以用C#和VB编译器和LINQ支持的新的数据格式化特性(data shaping features)来声明我们只想返回属性集合的一个子集,像下面这样来修改代码:

  

这将使得仅仅从数据库中返回这个子集(如在调试器中所示):
 

LINQ to SQL酷的是,在格式化数据时,我可以充分利用我的数据模型的关系。这使得我可以表达真正有用(而且非常高效)的数据查询。例如,如下的这个查询检索Product实体的ID和Name,该产品的订单总数,然后将每个产品的订单的利润加了起来。

上面这个表达式中,Revenue右边的属性是使用LINQ 提供的"Sum"扩展方法( extension method)。它使用了返回了每个产品的订单欺项作为参数的Lambda表达式(Lambda expression)。

LINQ to SQL非常智能,并且能够在求值时将以上的LINQ 表达式转换为如下的SQL语句:


 

以上的SQL语句使得所有的订单计算和产品利润计算在SQL server中完成,并且生成了从数据库中返回仅有如下结果集的数据(这使得它运行更加快):
 

然后我们可以将结果序列绑定到我们的GridView控件来生成一个漂亮的UI:
 

顺便提一下--为了防止你疑惑,在Vs2008中,写这些LINQ 格式的查询时会有完全的智能提示:

在上面的例子中,我声明了一个使用了对象初始化(object initialization )的匿名类型(anonymous type)来格式化和定义结果结构。真正酷的是,Vs2008提供了完全的智能感知,编译时检查,和当对这个匿名结果序列进行重构时的支持:

 

对查询结果分页

在Web开发时,最常见的一个需求就是能够高效地建立数据分页的UI。LINQ 提供了两个内置的扩展方法,它使得分页不但简便,而且高效--Skip()和Take()方法。

我们可以用下面的Skip()和Take()方法来声明我们只想从数据库中返回10条产品对象--从初始的产品记录开始(此处我们作为了一个参数)
 

注意上面我为何没在初始的产品查询声明时用Skip()和Take()操作符--而是稍后将它加到了查询中(当将数据绑定到GridView的DataSource时)。人们经常问我,“但是这不意味着该查询首先从数据库中查询出所有记录然后在中层层进行分页(这是不好的)?”,答案是否定的。原因就是LINQ用的是一个“延迟执行”的执行模式--它意味着直到当你试图遍历结果集时它才会执行。

这然后就会展示给我们一个产品页,仅仅显示了超过5条订单产品,显示动态计算出来的产品数据,而且它是可以通过Querystring参数进行分页:


 

注意:当使用SQL 2005时,LINQ to SQL将会在数据库中使用ROW_NUMBER()这个SQL函数来执行分页逻辑。这保证了当我们执行以上的代码时,从数据库中仅返回10条在当前页面上显示的我们需要显示的的数据:

这使得对大的数据表进行分页变得高效和容易。

总结

在本系列的下一篇帖子中,我将讲述一下如何向我们的数据模型中添加一个清晰的验证逻辑,并且讲述如何用它来减少每次我们进行更新,插入或删除数据时都会执行的业务验证。届时我会更深入地讲解延迟和即时加载的情景,如何使用<asp:LINADataSource>控件来支持显示的绑定ASP.NEt控件,优化并发冲突错误的解决方案,以及其他的一些知识。


希望这些对你有所帮助。

Scott