码迷,mamicode.com
首页 > 其他好文 > 详细

微软-创建数据访问层

时间:2015-06-10 23:49:29      阅读:266      评论:0      收藏:0      [点我收藏+]

标签:

简介

https://msdn.microsoft.com/zh-cn/cc964016

作为web 开发人员,我们的工作总是在和数据打交道。我们创建数据库来存储数据,编写代码来检索并修改数据,并创建Web 页面来收集和汇总数据。这是探讨在ASP.NET 2.0 中实现这些常用类型的技巧的系列教程中的首篇教程。我们从创建一个 软件架构 开始,包括使用Typed DataSet 的数据访问层(DAL) 、实现自定义业务规则的业务逻辑层(BLL) 和共享同一页面布局的ASP.NET 页面组成的表示层。一旦奠定了这个基础,我们接下来会转向报表,说明如何显示、汇总、收集和验证来自Web 应用程序的数据。这些教程力求简明,使用大量屏幕截图逐步教您直观地了解整个流程。每个教程都提供C# 和Visual Basic 版本,并且可以下载所使用的全部代码。(这篇教程内容非常冗长,但接下来会分几大部分进行介绍,使人更容易理解和消化。)

针对这些教程,我们将使用放在 App_Data 目录下 Northwind 数据库的 Microsoft SQL Server 2005 Express Edition 版本。除数据库文件外,App_Data 文件夹也包含创建该数据库的 SQL 脚本,以满足您想使用不同数据库版本的需求。如果愿意,这些脚本也可以直接从 Microsoft 下载 。如果您使用的是 Northwind 数据库的不同 SQL Server 版本,需要更新该应用程序的Web.config 文件中的NORTHWNDConnectionString 设置。这个Web 应用程序是使用 Visual Studio 2005 Professional Edition 创建的基于文件系统的 Web 站点项目。不过,所有的这些教程同样适用于Visual Studio 2005 免费版,即 Visual Web Developer 。
该教程从头开始,先创建数据访问层 (DAL) ,然后在第二篇教程中创建业务逻辑层(BLL) ,并在第三篇教程中进行页面布局和导航。随后的教程以前三篇教程为基础。在这篇教程中我们有很多内容要学习,现在就让我们打开Visual Studio 开始吧!

步骤1 : 创建一个 Web 项目并连接到数据库

在创建我们的数据访问层 (DAL) 之前,我们首先需要创建一个网站并安装我们的数据库。开始创建一个新的基于文件系统的ASP.NET 网站:从 File 菜单选择 New Web Site ,出现 New Web Site 对话框。选择 ASP.NET Web Site 模板,将Location 下拉列表设置成 File System ,然后为该网站选择一个文件夹,并将语言设置成C# 。

技术分享

图1 :创建一个基于文件系统的新网站

这将创建一个具有 Default.aspx ASP.NET 页面和 App_Data 文件夹的新网站。

创建好了网站,下一步是在 Visual Studio 的Server Explorer 中添加对该数据库的引用。通过在 Server Explorer 中添加数据库,您可以添加来自 Visual Studio 的表、存储过程、视图等。您还可以手动或通过Query Builder 直观地查看表数据或进行查询。而且,当我们为DAL 创建Typed DataSet 时,我们需要将 Visual Studio 指向需要建立 Typed DataSet 的数据库。当我们能够及时在那个点上提供这种连接信息时,Visual Studio 自动填充己在 Server Explorer 注册过的数据库的下拉列表。

将 Northwind 数据库添加到Server Explorer 的步骤取决于您是否使用App_Data 文件夹中的SQL Server 2005 Express Edition 数据库,或者是否有您想使用的Microsoft SQL Server 2000 或 2005 数据库服务器安装程序。

使用 App_Data 文件夹中的数据库

如果您没有 SQL Server 2000 或2005 数据库服务器可连接,或者您只想避免将该数据库添加到数据库服务器的麻烦,可以使用位于已下载网站源代码的App_Data 文件夹 中 的Northwind 数据库的 SQL Server 2005 Express Edition 版本(NORTHWND.MDF) 。

位于 App_Data 文件夹中的数据库将被自动添加到 Server Explorer 。假如您安装了SQL Server 2005 Express Edition ,在 Server Explorer 应该看到一个名为NORTHWND.MDF 的节点,可以展开并探究其表、视图、存储过程等(见图2 )。

App_Data 文件夹也可以存放 Microsoft Access .mdb 文件。同它们的 SQL Server 版本的数据库一样,这类文件将被自动添加到Server Explorer 。如果您不想使用任何 SQL Server 的数据库,您可以下载 Northwind 数据库文件的 Microsoft Access 版本 并加入 App_Data 目录。不过要记住,Access 数据库的特性不如SQL Server 丰富,且并不是为在网站环境下使用而设计的。另外,35 以后的教程将用到某些不被 Access 支持的数据库级特性。

连接到Microsoft SQL Server 2000 或2005 数据库服务器中的数据库

同样,您可能要连接到安装在数据库服务器上的Northwind 数据库。如果数据库服务器还没有安装Northwind 数据库,您必须先运行该教程下载文件中的安装脚本或直接从Microsoft 网站下载 Northwind 的 SQL Server 2000 版本和安装脚本 ,将其添加到数据库服务器。

一旦安装了该数据库,转到 Visual Studio 的Server Explorer ,右键单击 Data Connections 节点并选择 Add Connection 。如果没有找到 Server Explorer ,转入View / Server Explorer 或选择 Ctrl+Alt+S 。这时将出现 Add Connection 对话框,在这里可以指定要连接的服务器,认证信息和数据库名称。一旦成功配置了数据库连接信息,单击OK 按钮,该数据库将被作为一个节点添加到Data Connections 节点下面。您可以展开该数据库节点以探究其表、视图、存储过程等。

技术分享

图2 :在您的数据库服务器的 Northwind 数据库添加一个连接

步骤2 :创建数据访问层

在处理数据时,有种做法是将数据的特定逻辑直接内嵌到表示层(Web 应用程序中,ASP.NET 页面组成表示层)。这可以通过在 ASP.NET 页面的代码部分编写 ADO.NET 代码,或者在标记符部分使用 SqlDataSource 控件来完成。无论采取哪种形式,该方法都让数据访问逻辑与表示层紧密结合。不过,建议将数据访问与表示层隔离开来。这个分离层被称作数据访问层(DAL) ,通常作为一个单独的类库项目来实现。这种分层体系结构的优势得到了很好的论述,我们在该系列教程中也采用了此法。

有关基础数据源的所有代码,如创建到数据库的连接,发出SELECT 、INSERT 、UPDATEDELETE 命令等,都应位于 DAL 。表示层不应包含对这些数据访问代码的任何引用,而是通过调用DAL 来实现所有的数据访问请求。数据访问层通常包含访问基础数据库数据的方法。例如,Northwind 数据库提供ProductsCategories 表,记录要销售的产品及其他们所属的类别。我们的DAL 提供如下方法:

  • GetCategories():返回所有类别的信息
  • GetProducts():返回所有产品的信息
  • GetProductsByCategoryID(categoryID):返回属于某一指定类别的所有产品
  • GetProductByProductID(productID):返回某一产品的信息

调用时,这些方法将连接到数据库,进行适当的查询,并返回查询结果。重要的是返回这些结果的方式。这些方法可以简单返回一个由数据库查询填充的DataSet 或 DataReader ,但理想的是应使用强类型的对象来返回这些结果。强类型对象指的是编译时对其schema 进行严格定义的对象。相反,弱类型的对象指的是只有在运行时才知道其schema 的对象。

例如,DataReader 和DataSet (默认)是弱类型对象,因为它们的 schema 是由用来填充它们的数据库查询返回的列来定义的。要访问弱类型DataTable 中的某列,需要使用如下语法:DataTable.Rows[index]["columnName"] 。我们需要使用字符串或序号索引访问列名这一点就显示了该例中DataTable 的弱类型特性。另一方面,强类型的 DataTable 有各自作为属性实现的列,生成如下代码:DataTable.Rows[index].columnName

要返回强类型对象,开发人员可以创建他们自己的自定义业务对象或使用Typed DataSet 。一个业务对象由开发人员实现成一个类,该类的属性通常反映出该业务对象表示的基础数据库表的列。Typed DataSet 是由 Visual Studio 基于数据库 schema 为您生成的一个类,其成员都是强类型的。Typed DataSet 本身包括了拓展ADO.NET DataSet 、DataTable 和 DataRow 类的类。除强类型 DataTable 外,Typed DataSet 现在还包括 TableAdapter ,该类具有填充 DataSet 的DataTable 以及将 DataTable 中所作修改传回数据库的方法。

注意:有关使用Typed DataSet 与自定义业务对象的优劣比较的更多信息,请参见设计数据层组件并在层间传递数据

我们对这些教程的体系结构使用强类型的DataSet 。图 3 说明了使用 Typed DataSet 的应用程序不同层间的工作流程。

技术分享

图3 :所有的数据访问代码都在 DAL 中定义

创建一个 Typed DataSet 和Table Adapter

要创建我们的 DAL ,要先将一个Typed DataSet 添加到我们的项目。为此,右键单击 Solution Explorer 中的项目节点,并选择 Add a New Item 。从模板列表中选择DataSet 选项,并将其命名为Northwind.xsd

技术分享

图4 :选择添加一个新的DataSet 到您的项目

单击 Add 后,出现提示添加DataSet 到App_Code 文件夹,选择Yes 。接着出现 Typed DataSet 设计器并将启动 TableAdapter Configuration Wizard ,允许您将您的第一个 TableAdapter 添加到 Typed DataSet 。

Typed DataSet 充当一个强类型的数据集;它由强类型的DataTable 实例组成,每个实例依次由强类型的 DataRow 实例组成。我们将为该教程系列所需的每个基础数据库表创建一个强类型的DataTable 。我们开始为Products 表创建 DataTable 。

要记住,强类型的 DataTable 不包含有关如何从其基础数据库表访问数据的任何信息。为了检索数据以填充DataTable ,我们使用一个 TableAdapter 类,作为我们的数据访问层。对于我们的Products 数据表,TableAdapter 将包含 GetProducts()GetProductByCategoryID(categoryID) 等方法,我们将从表示层调用它们。DataTable 的作用是充当用来在层间传送数据的强类型对象。

TableAdapter Configuration Wizard 从提示您选择要操作的数据库开始。下拉列表显示出Server Explorer 中的数据库。如果您没有把 Northwind 数据库添加到 Server Explorer ,此时单击 New Connection 按钮进行该操作。

技术分享

图5 :从下拉列表中选择Northwind 数据库

选择了数据库单击 Next 后, 将会问您是否希望在Web.config 文件中保存连接字符串。保存了连接字符串,就避免了将连接字符串写在TableAdapter 类的代码中。这样一旦以后连接字符串信息改变,要做的工作也变得简单。如果选择将连接字符串保存到放在<connectionStrings> 区域的配置文件,以后可以通过 IIS GUI Admin Tool 内的新 ASP.NET 2.0 Property Page 选择加密 以改进安全性或修改。这样更适合管理员工作。

技术分享

图6 :保存连接字符串到Web.config

接下来,我们需要为第一个强类型的DataTable 定义 schema ,并为我们的 TableAdapter 提供填充强类型的 DataSet 时所用到的第一个方法。通过创建一个查询,可同时完成这两个步骤,这个查询能够返回我们希望映射我们的DataTable 中的数据表的列。在向导结束时,我们将为该查询提供一个方法名称。完成后,就可以从我们的表示层调用该方法。此法将执行所定义的查询并填充强类型的DataTable 。

要开始定义 SQL 查询,就必须先说明希望 TableAdapter 进行查询的方式。我们可以使用一个 ad-hoc SQL 语句,创建一个新的存储过程,或使用现有的存储过程。对于本系列教程,我们将使用ad-hoc SQL 语句。有关使用存储过程的例子,请参见Brian Noyes 的文章:使用 Visual Studio 2005 DataSet Designer 构建一个数据访问层

技术分享

图7 :使用ad-hoc SQL 语句查询数据

此时,我们可以手动输入 SQL 查询。在TableAdapter 创建第一个方法时,您通常希望查询返回需要在相应DataTable 中存放的那些列。这可以通过创建返回来自Products 表的所有列和行的查询实现:

技术分享

图8 :在文本框输入SQL 查询

也可以使用 Query Builder 用图像构建查询,如图 9 所示。

技术分享

图9 :通过Query Editor 用图像创建查询

创建查询之后,要在转到下一屏幕之前,单击Advanced Options 按钮。在 Web Site 项目中,“Generate Insert, Update, and Delete statements ”是默认选择的唯一高级选项;如果从Class Library 或Windows Project 运行该向导,“Use optimistic concurrency (使用并发优化)”选项将也被选中。现在暂不勾选该选项。我们将在以后的教程中详细介绍并发优化。

技术分享

图10 :只选择“Generate Insert, Update, and Delete statements ”选项

核实了高级选项后,单击 Next 进入最后一个屏幕。这时会询问我们选择那些方法添加到TableAdapter 。填充数据的模式有两种:

  • Fill a DataTable:通过这种途径创建的方法将 DataTable 作为一个参数,并根据查询结果对其进行填充。例如,ADO.NET DataAdapter 类使用其 Fill()方法实现了该模式。
  • Return a DataTable:通过这种途径创建的方法为您创建并填充DataTable ,并将 DataTable 作为方法的返回值。

您可以在 TableAdapter 实现这两种模式中的一种或两种。也可以重命名此处所提供的方法。即使我们在整个教程中只使用第二种模式,我们也可以同时选中两个复选框。并且将更通用的GetData 法重命名为GetProducts

如果选中最后一个复选框"GenerateDBDirectMethods" ,会为 TableAdapter 创建 Insert()Update()Delete() 方法。如果没有选中该选项,所有的更新则需要通过TableAdapter 唯一的 Update() 方法进行,该方法接受 Typed DataSet 、DataTable 、单个DataRow 或 DataRow 数组。(如果没有选中图 9 高级选项中的"Generate Insert, Update, and Delete statements" 选项,该复选框的设置则不起作用。)在这里,我们勾选这个复选框。

技术分享

图11 :将方法名称GetData 改为GetProducts

单击 Finish 结束向导。向导关闭后,返回到显示我们刚创建的DataTable 的 DataSet 设计器。可以看到Products 数据表中各列的列表(ProductIDProductName 等),以及ProductsTableAdapter 的方法(Fill()GetProducts() )。

技术分享

图12 :Products DataTable 和ProductsTableAdapter 已被添加到Typed DataSet

这里,我们拥有一个带有单个 DataTable (Northwind.Products) 的Typed DataSet ,还有一个提供 GetProducts() 方法的强类型DataAdapter 类 (NorthwindTableAdapters.ProductsTableAdapter) 。这些对象可用来通过类似以下代码访问所有产品的列表:

NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
    new NorthwindTableAdapters.ProductsTableAdapter();
Northwind.ProductsDataTable products;

products = productsAdapter.GetProducts();

foreach (Northwind.ProductsRow productRow in products)
    Response.Write("Product: " + productRow.ProductName + "<br />");

该代码不要求我们去编写任何针对数据访问的代码。我们不必生成任何ADO.NET 类的实例,不必指定任何连接字符串、SQL 查询或存储过程。TableAdapter 会为我们提供底层的数据访问代码。

本示例中所使用的每个对象都是强类型的,允许Visual Studio 提供 IntelliSense (智能感知)和编译时类型检查。而且,最好的是,TableAdapter 返回的DataTable 可以绑定到 ASP.NET Web 数据控件,如 GridView 、DetailsView 、DropDownList 、CheckBoxList 和其它。下面举例说明如何在Page_Load 事件处理程序仅用三行代码就将 GetProducts() 法返回的 DataTable 绑定到 GridView 。

AllProducts.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="AllProducts.aspx.cs"
    Inherits="AllProducts" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>View All Products in a GridView</title>
    <link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <h2>
            All Products</h2>
        <p>
            <asp:GridView ID="GridView1" runat="server"
             CssClass="DataWebControlStyle">
               <HeaderStyle CssClass="HeaderStyle" />
               <AlternatingRowStyle CssClass="AlternatingRowStyle" />
            </asp:GridView>
             </p>

    </div>
    </form>
</body>
</html>

AllProducts.aspx.cs

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;

public partial class AllProducts : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        ProductsTableAdapter productsAdapter = new
         ProductsTableAdapter();
        GridView1.DataSource = productsAdapter.GetProducts();
        GridView1.DataBind();
    }
}

技术分享

图13 :显示在GridView 中的产品列表

尽管本例还需要我们在 ASP.NET 页面的 Page_Load 事件处理程序中编写三行代码,不过在以后的教程中我们将详细介绍如何使用ObjectDataSource ,用声明的方式获取 DAL 数据。使用 ObjectDataSource 我们不必编写代码也可以进行分页和排序!

 

 

步骤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

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Beverages.aspx.cs"
    Inherits="Beverages" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
    <link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <h2>Beverages</h2>
        <p>
            <asp:GridView ID="GridView1" runat="server"
             CssClass="DataWebControlStyle">
               <HeaderStyle CssClass="HeaderStyle" />
               <AlternatingRowStyle CssClass="AlternatingRowStyle" />
            </asp:GridView>
             </p>
    </div>
    </form>
</body>
</html>

Beverages.aspx.cs

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;

public partial class Beverages : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        ProductsTableAdapter productsAdapter = new
         ProductsTableAdapter();
        GridView1.DataSource =
          productsAdapter.GetProductsByCategoryID(1);
        GridView1.DataBind();
    }
}

技术分享

图20 :显示出的饮料类别中的产品

步骤4 : 数据的添加、更新和删除

添加、更新和删除数据的常用模式有两种。第一种模式,我称之为数据库直接模式,当涉及的方法被调用时,会向数据库发送一个INSERTUPDATEDELETE 命令,该命令只对单个数据库记录进行操作。这些方法通常通过一系列的标量值(整数、字符串、布尔类型、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 的 InsertCommandUpdateCommandDeleteCommand 属性来向数据库发布它们的INSERTUPDATEDELETE 命令。您可以通过单击 DataSet Designer 中的 TableAdapter 并转入 Properties 窗口来检查和修改InsertCommandUpdateCommandDeleteCommand 属性。(要确信您已经选择了 TableAdapter 并确保 ProductsTableAdapter 对象是 Properties 窗口中下拉列表中的被选中的选项。)

技术分享

图23 :TableAdapter 具有的 InsertCommandUpdateCommandDeleteCommand 属性

要检查或修改这些数据库命令的任何属性,单击CommandText 子属性即可弹出Query Builder 。

技术分享

图24 :在Query Builder 配置INSERTUPDATEDELETE 语句

下面的代码示例说明了如何使用批量更新模式使所有没有断货的、库存小于等于25 件的产品的价格提高一倍:

NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
  new NorthwindTableAdapters.ProductsTableAdapter();

// For each product, double its price if it is not discontinued and
// there are 25 items in stock or less
Northwind.ProductsDataTable products = productsAdapter.GetProducts();
foreach (Northwind.ProductsRow product in products)
   if (!product.Discontinued && product.UnitsInStock <= 25)
      product.UnitPrice *= 2;

// Update the products
productsAdapter.Update(products);

下面的代码表明如何使用数据库直接模式通过编码实现删除、更新某个产品,然后添加某个新产品:

NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
    new NorthwindTableAdapters.ProductsTableAdapter();

// Delete the product with ProductID 3
productsAdapter.Delete(3);

// Update Chai (ProductID of 1), setting the UnitsOnOrder to 15
productsAdapter.Update("Chai", 1, 1, "10 boxes x 20 bags",
  18.0m, 39, 15, 10, false, 1);

// Add a new product
productsAdapter.Insert("New Product", 1, 1,
  "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 表添加新行的方法

下一个屏幕上出现 InsertCommandCommandText 。在查询末尾添加SELECT SCOPE_IDENTITY() 语句,这样将返回同一范围内添加到IDENTITY 列的最后一个identity 值。(参见技术文档 了解有关SCOPE_IDENTITY() 的更多信息以及您可能希望使用 SCOPE_IDENTITY() 代替 @@IDENTITY 的原因。)确保您在添加SELECT 语句之前用一个分号结束INSERT 语句。

技术分享

图26 :增大返回SCOPE_IDENTITY() 值的查询范围

最后,将新方法命名为InsertProduct

技术分享

图27 :设置新方法的名称为InsertProduct

当您返回到 DataSet 设计器时,您会发现ProductsTableAdapter 包含了新方法:InsertProduct 。如果对应 Products 表中的每个列,这个新方法没有对应的参数,可能就是您忘记用分号来终止INSERT 语句。配置InsertProduct 方法并确保您使用了分号来终止INSERTSELECT 语句。

默认状态下,添加方法调用的是非查询方法,意味着它们返回的是受影响的行数。不过,我们希望InsertProduct 方法返回查询返回的值,而不是受影响的行数。为此,将InsertProduct 方法的 ExecuteMode 的属性修改为Scalar

技术分享

图28 :ExecuteMode 属性更改为Scalar

下面的代码表明了运行中的这个新的InsertProduct 方法:

NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
    new NorthwindTableAdapters.ProductsTableAdapter();

// Add a new product
int new_productID = Convert.ToInt32(productsAdapter.InsertProduct
    ("New Product", 1, 1, "12 tins per carton", 14.95m, 10, 0, 10, false));

// On second thought, delete the product
productsAdapter.Delete(new_productID);



步骤 5:完成数据访问层

注意,ProductsTableAdapters 类返回Products 表的 CategoryIDSupplierID 值,但不包含Categories 表的 CategoryName 列,或 Suppliers 表的 CompanyName 列,尽管在显示产品信息时我们可能也希望显示这些列。我们可以扩充TableAdapter 初始方法GetProducts() ,使其包括 CategoryNameCompanyName 列值,这将更新强类型的 DataTable ,使其同样包含这些新列。

但是,这样就会出现一个问题,因为TableAdapter 的添加、更新和删除方法并不是基于这个初始方法。幸运的是,自动生成的添加、更新和删除方法不受SELECT 子句中的子查询影响。通过把对 CategoriesSuppliers 的查询作为子查询添加到我们原来的查询语句中,而不是使用JOIN 连接,我们可以避免重写这些用来修改数据的方法。右键单击ProductsTableAdapter 中的 GetProducts() 方法并选择Configure 。随后将 SELECT 子句修改如下:

SELECT     ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued,
(SELECT CategoryName FROM Categories
WHERE Categories.CategoryID = Products.CategoryID) as CategoryName,
(SELECT CompanyName FROM Suppliers
WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName
FROM         Products

技术分享

图29 :GetProducts() 方法更新SELECT 语句

使用这个新查询更新 GetProducts() 方法后,DataTable 将包含下面两个新列:CategoryNameSupplierName

技术分享

图30 :Products 数据表有两个新列

花点时间也来更新 GetProductsByCategoryID(categoryID) 方法中的 SELECT 子句。

如果使用 JOIN 语法更新 GetProducts() 中的 SELECT ,DataSet 设计器将不能使用数据库直接模式自动生成数据插入、更新和删除的方法。您不得不手动的生成这些方法,就好象在本教程早先时候我们对InsertProduct 方法的做法一样。另外,如果您希望使用批量更新模式,就必须手动提供InsertCommandUpdateCommandDeleteCommand 属性值。

添加剩余的TableAdapters

至今为止,我们只是介绍了单个数据库表的单个TableAdapter 。但是,Northwind 数据库包含需要我们在我们的 Web 应用程序中操作的几个相关表。一个 Typed DataSet 可以包含多个相关的 DataTable 。因此,要完成我们的 DAL ,还需要为我们在这些教程中用到的其它表添加DataTable 。要添加新的 TableAdapter 到 Typed DataSet ,打开 DataSet Designer ,右键单击Designer 并选择 Add / TableAdapter 。这将创建一个新的 DataTable 和 TableAdapter ,并引导你完成我们在前面教程所讨论的配置向导。

花几分钟的时间,用下面的查询语句创建对应的TableAdapters 及其方法。注意,ProductsTableAdapter 中的查询包括子查询,以获取每个产品的类别和供应商名称这些信息。另外,如果您一直都在跟着教程操作,那您就已经添加了ProductsTableAdapter 类的 GetProducts()GetProductsByCategoryID(categoryID) 方法。

  • ProductsTableAdapter
    • GetProducts: SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT CategoryName FROM Categories WHERE Categories.CategoryID = Products.CategoryID) as CategoryName, (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName FROM Products
    • GetProductsByCategoryID: SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT CategoryName FROM Categories WHERE Categories.CategoryID = Products.CategoryID) as CategoryName, (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName FROM Products WHERE CategoryID = @CategoryID
    • GetProductsBySupplierID: SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT CategoryName FROM Categories WHERE Categories.CategoryID = Products.CategoryID) as CategoryName, (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName FROM Products WHERE SupplierID = @SupplierID
    • GetProductByProductID: SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT CategoryName FROM Categories WHERE Categories.CategoryID = Products.CategoryID) as CategoryName, (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName FROM Products WHERE ProductID = @ProductID
  • CategoriesTableAdapter
    • GetCategories: SELECT CategoryID, CategoryName, Description FROM Categories
    • GetCategoryByCategoryID: SELECT CategoryID, CategoryName, Description FROM Categories WHERE CategoryID = @CategoryID
  • SuppliersTableAdapter
    • GetSuppliers: SELECT SupplierID, CompanyName, Address, City, Country, Phone FROM Suppliers
    • GetSuppliersByCountry: SELECT SupplierID, CompanyName, Address, City, Country, Phone FROM Suppliers WHERE Country = @Country
    • GetSupplierBySupplierID: SELECT SupplierID, CompanyName, Address, City, Country, Phone FROM Suppliers WHERE SupplierID = @SupplierID
  • EmployeesTableAdapter
    • GetEmployees: SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country FROM Employees
    • GetEmployeesByManager: SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country FROM Employees WHERE ReportsTo = @ManagerID
    • GetEmployeeByEmployeeID: SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country FROM Employees WHERE EmployeeID = @EmployeeID

技术分享

图31 :添加了四个TableAdapter 的 DataSet 设计器

向 DAL 添加自定义代码

添加到 Typed DataSet 的TableAdapter 和 DataTable 由 XML Schema Definition 文件 (Northwind.xsd) 来描述。通过右键单击 Solution Explorer 中的Northwind.xsd 文件并选择View Code ,可以查看该 schema 的信息。

技术分享

图32 :针对Northwinds Typed DataSet 的 XML Schema Definition (XSD) 文件

编译或运行时(如果需要),该schema 信息在设计时被译成 C# 或 Visual Basic 代码,此时您可以使用调试器进行调试。要查看这个自动生成的代码,转入Class View 并找到TableAdapter 或 Typed DataSet 类。如果在屏幕上看不到 Class View ,转入View 菜单并选中它,或按下 Ctrl+Shift+C 。从 Class View 上可以看到 Typed DataSet 和TableAdapter 类的属性、方法和事件。要查看某个方法的代码,双击Class View 中该方法的名称或右键单击它并选择 Go To Definition 。

技术分享

图33 : 通过选择Class View 的 Selecting Go To Definition 检查自动生成的代码

尽管自动生成的代码可以节省很多时间,但是它们通常都是通用代码,需要自定义来满足应用程序的特定要求。可是,拓展自动生成代码的风险在于生成代码的工具可以决定何时“再生成”而覆盖了您的自定义操作。有了.NET 2.0 的新的部分类概念,我们可以非常简单的将一个类的定义分写在几个文件中。这样我们能够添加自己的方法、属性和事件到自动生成的类,而不必担心Visual Studio 覆盖了我们的自定义内容。

为了说明如何自定义 DAL ,我们现在把GetProducts() 方法添加到SuppliersRow 类。SuppliersRow 类在Suppliers 表呈现一条记录;每个供应商可以提供零到多个产品,这样GetProducts() 将返回指定供应商的那些产品信息。要做到这些,需要在App_Code 文件夹中创建一个名为 SuppliersRow.cs 的新的类文件,然后在其中添加下列代码:

using System;
using System.Data;
using NorthwindTableAdapters;

public partial class Northwind
{
    public partial class SuppliersRow
    {
        public Northwind.ProductsDataTable GetProducts()
        {
            ProductsTableAdapter productsAdapter =
             new ProductsTableAdapter();
            return
              productsAdapter.GetProductsBySupplierID(this.SupplierID);
        }
    }
}

该部分类指示编译器创建 Northwind.SuppliersRow 类时包含我们刚定义的 GetProducts() 方法。如果您 build 您的项目,然后返回到 Class View ,将看见GetProducts() 现在被列为Northwind.SuppliersRow 的方法。

技术分享

图34 G:GetProducts() 方法现在是Northwind.SuppliersRow 类的一部分

GetProducts() 方法现在可用来列举某供应商的全套产品,如下面的代码所示:

NorthwindTableAdapters.SuppliersTableAdapter suppliersAdapter =
    new NorthwindTableAdapters.SuppliersTableAdapter();

// Get all of the suppliers
Northwind.SuppliersDataTable suppliers =
  suppliersAdapter.GetSuppliers();

// Enumerate the suppliers
foreach (Northwind.SuppliersRow supplier in suppliers)
{
    Response.Write("Supplier: " + supplier.CompanyName);
    Response.Write("<ul>");

    // List the products for this supplier
    Northwind.ProductsDataTable products = supplier.GetProducts();
    foreach (Northwind.ProductsRow product in products)
        Response.Write("<li>" + product.ProductName + "</li>");

    Response.Write("</ul><p> </p>");
}

该数据也可以在任何 ASP.NET 的Web 数据控件中显示。以下页面使用了具有两个字段的GridView 控件:

  • 显示每个供应商名称的 BoundField 和
  • 一个 TemplateField ,它包含了一个 BulletedList 控件,该控件与每个供应商的 GetProducts() 方法的返回结果绑定。

我们会在后面的教程中探讨如何显示这种主/ 明细报表。就目前而言,该示例旨在说明添加到Northwind.SuppliersRow 类的自定义方法的使用。

SuppliersAndProducts.aspx

<%@ Page Language="C#" CodeFile="SuppliersAndProducts.aspx.cs"
    AutoEventWireup="true" Inherits="SuppliersAndProducts" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
    <link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <h2>
            Suppliers and Their Products</h2>
        <p>
            <asp:GridView ID="GridView1" runat="server"
             AutoGenerateColumns="False"
             CssClass="DataWebControlStyle">
                <HeaderStyle CssClass="HeaderStyle" />
                <AlternatingRowStyle CssClass="AlternatingRowStyle" />
                <Columns>
                    <asp:BoundField DataField="CompanyName"
                      HeaderText="Supplier" />
                    <asp:TemplateField HeaderText="Products">
                        <ItemTemplate>
                            <asp:BulletedList ID="BulletedList1"
                             runat="server" 
       DataSource="<%# ((Northwind.SuppliersRow) ((System.Data.DataRowView) Container.DataItem).Row).GetProducts() %>"
                                 DataTextField="ProductName">
                            </asp:BulletedList>
                        </ItemTemplate>
                    </asp:TemplateField>
                </Columns>
            </asp:GridView>
             </p>

    </div>
    </form>
</body>
</html>

SuppliersAndProducts.aspx.cs

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;

public partial class SuppliersAndProducts : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        SuppliersTableAdapter suppliersAdapter = new
          SuppliersTableAdapter();
        GridView1.DataSource = suppliersAdapter.GetSuppliers();
        GridView1.DataBind();
    }
}

技术分享

图35 :供应商的公司名列在左列,他们的产品在右列

小结

创建DAL 应该是开发一个Web 应用程序的第一步,这要在开始创建您的表示层之前进行。通过Visual Studio ,创建一个基于Typed DataSet 的DAL 就成为一项不需要编写一行代码,在10 到15 分钟内就可以完成的任务。教程后面的内容仍旧围绕DAL 进行。 下一教程 我们将定义几个业务规则并看看如何在单个业务逻辑层中实现它们。

快乐编程!

微软-创建数据访问层

标签:

原文地址:http://www.cnblogs.com/netserver/p/4567418.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!