ASP.NET数据库访问系列教程
本教程深入探讨了基于ASP.NET 2.0技术的数据库访问方法和模式。这些介绍非常简明,并且提供了一步步的指导和大量的截屏。
该系列教程包括:
- 概述篇
- 基础报表
- 主/明细报表
- 自定义格式报表
- 编辑,插入和删除数据
- 分页和排序、自定义按钮事件
- 使用DataList和Repeater控件显示数据
- 使用DataList和Repeater进行数据筛选
- 通过DataList编辑和删除数据
- 使用DataList和Repeater控件进行分页和排序
- 自定义DataList和Repeater控件的按钮事件
- 从ASP.NET页面直接访问数据库
- 扩展GridView控件
- 操作二进制文件
- 数据缓存
- 基于数据库的站点地图
- 批量数据处理
- 高级数据库访问操作
#1 概述篇-创建数据访问层(中)
该教程从头开始使用 Typed DataSet(强类型 DataSet)创建数据访问层 (DAL),以访问数据库中的信息。
步骤3 :向数据访问层添加带参数的方法
我们的 ProductsTableAdapter 类此时有且只有一个方法:GetProducts() ,它返回数据库内的所有产品信息。尽管能够处理所有产品是肯定有用的,不过有时候我们希望只检索特定产品或属于某个类别的所有产品的信息。要在我们的数据访问层中添加这个功能,可以通过带参数的方法添加到TableAdapter 来实现。
现在我们添加 GetProductsByCategoryID(categoryID) 方法。向 DAL 添加一个新方法,返回到 DataSet Designer ,右键单击ProductsTableAdapter 区域,并选择Add Query 。
图14 :右键单击TableAdapter 并选择 Add Query
我们首先被问到是否希望使用 ad-hoc SQL 语句或创建或使用现有的存储过程来访问数据库。再次选择使用ad-hoc SQL 语句。接下来会询问我们想使用的 SQL 查询类型。由于我们希望返回属于某一指定类别的所有产品信息,我们想编写一个返回行的SELECT 语句。
图15 :选择创建返回行的SELECT 语句
下一步是定义用来访问数据的 SQL 查询。由于我们希望只返回属于某一指定类别的那些产品信息,我使用的是GetProducts() 中的同一个 SELECT 语句,但是添加了下面的 WHERE 子句:WHERE CategoryID = @CategoryID 。@CategoryID 参数向TableAdapter 向导表明,我们正在创建的方法将要求一个对应类型的输入参数(也就是说,一个可为Null 的整数)。
图16 :查询只返回指定类别中的 产品信息
在最后一个步骤,我们可以选择要使用的数据访问模式,并自定义所生成方法的名称。将Fill 模式重命名为FillByCategoryID ,并使用 GetProductsByCategoryID 作为返回 DataTable 返回模式(GetX 方法)的名称。
图17 :为TableAdapter 方法选择名称
向导结束后,DataSet 设计器包含新的TableAdapter 方法。
图18 :产品现在可按类别进行查询
使用同样的技术再添加 GetProductByProductID(productID) 方法
可以直接从 DataSet 设计器对这些带参数的查询进行检验。右键单击TableAdapter 中的方法并选择 Preview Data 。然后输入用于参数的值并单击Preview 。
图19 :显示出的属于饮料类别的产品信息
通过 DAL 中的 GetProductsByCategoryID(categoryID) 方法,我们现在可以创建一个只显示指定类别的那些产品的ASP.NET 页面。下面举例显示CategoryID 为1 的饮料类别中的所有产品信息。
Beverages.aspx
1 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Beverages.aspx.cs"
2 Inherits="Beverages" %>
3
4 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
7 <html xmlns="http://www.w3.org/1999/xhtml" >
8 <head runat="server">
9 <title>Untitled Page</title>
10 <link href="Styles.css" rel="stylesheet" type="text/css" />
11 </head>
12 <body>
13 <form id="form1" runat="server">
14 <div>
15 <h2>Beverages</h2>
16 <p>
17 <asp:GridView ID="GridView1" runat="server"
18 CssClass="DataWebControlStyle">
19 <HeaderStyle CssClass="HeaderStyle" />
20 <AlternatingRowStyle CssClass="AlternatingRowStyle" />
21 </asp:GridView>
22 </p>
23 </div>
24 </form>
25 </body>
26 </html>
Beverages.aspx.cs
1 using System;
2 using System.Data;
3 using System.Configuration;
4 using System.Collections;
5 using System.Web;
6 using System.Web.Security;
7 using System.Web.UI;
8 using System.Web.UI.WebControls;
9 using System.Web.UI.WebControls.WebParts;
10 using System.Web.UI.HtmlControls;
11 using NorthwindTableAdapters;
12
13 public partial class Beverages : System.Web.UI.Page
14 {
15 protected void Page_Load(object sender, EventArgs e)
16 {
17 ProductsTableAdapter productsAdapter = new
18 ProductsTableAdapter();
19 GridView1.DataSource =
20 productsAdapter.GetProductsByCategoryID(1);
21 GridView1.DataBind();
22 }
23 }
图20 :显示出的饮料类别中的产品
步骤4 : 数据的添加、更新和删除
添加、更新和删除数据的常用模式有两种。第一种模式,我称之为数据库直接模式,当涉及的方法被调用时,会向数据库发送一个INSERT 、UPDATE 或DELETE 命令,该命令只对单个数据库记录进行操作。这些方法通常通过一系列的标量值(整数、字符串、布尔类型、DateTimes 等)来传递参数,这些值与要添加、更新或删除的值相对应。例如,采用这种模式对Products 表进行操作,删除方法将采用一个整数参数,指明要删除的记录的ProductID ,而添加法将对 ProductName 采用字符串,对 UnitPrice 采用十进制,对 UnitsOnStock 采用整数值等。
图21 :每个添加、更新和删除请求被立即送达数据库
我把另一种模式称为批量更新模式,就是在一次方法调用中更新整个DataSet 、DataTable 、或 DataRows 集合。通过这种模式,开发人员在 DataTable 中删除、添加并修改 DataRow ,然后将那些 DataRow 或 DataTable 传递给一个更新方法。该方法随后列举传入的DataRow ,确定它们是否要进行修改、添加或删除(通过DataRow 的RowState 属性 值),并为每条记录发出适当的数据库请求。
图22 :调用更新方法时,所有更改都和数据库保持同步
TableAdapter 默认采用的是批量更新模式,但也支持数据库直接模式。由于创建我们的TableAdapter 时选择了 Advanced Properties 中的“Generate Insert, Update, and Delete statements ”选项,所以 ProductsTableAdapter 包含一个实现批量更新模式的 Update() 方法。具体点说,TableAdapter 包含 Update() 方法,可以传入一个强类型的 DataTable ,即 Typed DataSet ,或一个或多个 DataRow 传递。如果您在首次创建 TableAdapter 时选中了“GenerateDBDirectMethods ”复选框,数据库直接模式也可以通过Insert() 、Update() 和Delete() 方法来实现。
这两种数据修改模式都使用 TableAdapter 的 InsertCommand 、UpdateCommand 和DeleteCommand 属性来向数据库发布它们的INSERT 、UPDATE 和DELETE 命令。您可以通过单击 DataSet Designer 中的 TableAdapter 并转入 Properties 窗口来检查和修改InsertCommand 、UpdateCommand 和DeleteCommand 属性。(要确信您已经选择了 TableAdapter 并确保 ProductsTableAdapter 对象是 Properties 窗口中下拉列表中的被选中的选项。)
图23 :TableAdapter 具有的 InsertCommand 、UpdateCommand 和 DeleteCommand 属性
要检查或修改这些数据库命令的任何属性,单击CommandText 子属性即可弹出Query Builder 。
图24 :在Query Builder 配置INSERT 、UPDATE 和DELETE 语句
下面的代码示例说明了如何使用批量更新模式使所有没有断货的、库存小于等于25 件的产品的价格提高一倍:
1 NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
2 new NorthwindTableAdapters.ProductsTableAdapter();
3
4 // For each product, double its price if it is not discontinued and
5 // there are 25 items in stock or less
6 Northwind.ProductsDataTable products = productsAdapter.GetProducts();
7 foreach (Northwind.ProductsRow product in products)
8 if (!product.Discontinued && product.UnitsInStock <= 25)
9 product.UnitPrice *= 2;
10
11 // Update the products
12 productsAdapter.Update(products);
下面的代码表明如何使用数据库直接模式通过编码实现删除、更新某个产品,然后添加某个新产品:
1 NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
2 new NorthwindTableAdapters.ProductsTableAdapter();
3
4 // Delete the product with ProductID 3
5 productsAdapter.Delete(3);
6
7 // Update Chai (ProductID of 1), setting the UnitsOnOrder to 15
8 productsAdapter.Update("Chai", 1, 1, "10 boxes x 20 bags",
9 18.0m, 39, 15, 10, false, 1);
10
11 // Add a new product
12 productsAdapter.Insert("New Product", 1, 1,
13 "12 tins per carton", 14.95m, 15, 0, 10, false);
创建自定义Insert 、Update 和Delete 方法
由数据库直接方法创建的 Insert() 、Update() 和Delete() 方法有点麻烦,尤其是对于那些有许多列的表。看前面的代码示例,没有IntelliSense 的帮助,Products 表的列与Update() 和 Insert() 方法的每个输入参数的映射关系就很不明显。有时候我们可能只想更新一个或两个列,或者需要一个自定义Insert() 方法,该方法可能返回新添加记录的IDENTITY (自动递增)字段的值。
要创建这样的自定义方法,返回到DataSet Designer 。右键单击 TableAdapter 并选择 Add Query ,返回 TableAdapter 向导。在第二个屏幕上,我们可以指明要创建的查询类型。现在我们创建一个添加新产品并返回新加记录的ProductID 的值的方法。因此,选择创建一个 INSERT 查询。
图25 :创建一个向Products 表添加新行的方法
下一个屏幕上出现 InsertCommand 的 CommandText 。在查询末尾添加SELECT SCOPE_IDENTITY() 语句,这样将返回同一范围内添加到IDENTITY 列的最后一个identity 值。(参见技术文档 了解有关SCOPE_IDENTITY() 的更多信息以及您可能希望使用 SCOPE_IDENTITY() 代替 @@IDENTITY 的原因。)确保您在添加SELECT 语句之前用一个分号结束INSERT 语句。(Scope_identity:只返回插入到当前作用域中的值. @@identity 返回在当前会话中的任何表内所生成的最后一个标识值,不受限于特定的作用域.)
图26 :增大返回SCOPE_IDENTITY() 值的查询范围
最后,将新方法命名为InsertProduct 。
图27 :设置新方法的名称为InsertProduct
当您返回到 DataSet 设计器时,您会发现ProductsTableAdapter 包含了新方法:InsertProduct 。如果对应 Products 表中的每个列,这个新方法没有对应的参数,可能就是您忘记用分号来终止INSERT 语句。配置InsertProduct 方法并确保您使用了分号来终止INSERT 和 SELECT 语句。
默认状态下,添加方法调用的是非查询方法,意味着它们返回的是受影响的行数。不过,我们希望InsertProduct 方法返回查询返回的值,而不是受影响的行数。为此,将InsertProduct 方法的 ExecuteMode 的属性修改为Scalar 。
图28 :将 ExecuteMode 属性更改为Scalar
下面的代码表明了运行中的这个新的InsertProduct 方法:
1 NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
2 new NorthwindTableAdapters.ProductsTableAdapter();
3
4 // Add a new product
5 int new_productID = Convert.ToInt32(productsAdapter.InsertProduct
6 ("New Product", 1, 1, "12 tins per carton", 14.95m, 10, 0, 10, false));
7
8 // On second thought, delete the product
9 productsAdapter.Delete(new_productID);