创建数据访问层

本文档是 Visual Basic 教程 (切换到 Visual C# 教程)

该教程从头开始使用 Typed DataSet(强类型 DataSet)创建数据访问层 (DAL),以访问数据库中的信息。

« 前一篇教程  |  下一篇教程 »

步骤 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数据表有两个新列

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

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

添加剩余的 TableAdapters

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

花几分钟的时间,用下面的查询语句创建对应的 TableAdapters 及其方法。注意,ProductsTableAdapter中的查询包括子查询,以获取每个产品的类别和供应商名称这些信息。另外,如果您一直都在跟着教程操作,那您就已经添加了ProductsTableAdapterGetProducts()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.vb的新的类文件,然后在其中添加下列代码:

Imports NorthwindTableAdapters
Partial Public Class Northwind
    Partial Public Class SuppliersRow
        Public Function GetProducts() As Northwind.ProductsDataTable
            Dim productsAdapter As New ProductsTableAdapter
            Return productsAdapter.GetProductsBySupplierID(Me.SupplierID)
        End Function
    End Class
End Class

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

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

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

Dim suppliersAdapter As New NorthwindTableAdapters.SuppliersTableAdapter()
Dim suppliers As Northwind.SuppliersDataTable = suppliersAdapter.GetSuppliers()
For Each supplier As Northwind.SuppliersRow In suppliers
    Response.Write("Supplier: " & supplier.CompanyName)
    Response.Write("<ul>")
    Dim products As Northwind.ProductsDataTable = supplier.GetProducts()
    For Each product As Northwind.ProductsRow In products
        Response.Write("<li>" & product.ProductName & "</li>")
    Next
    Response.Write("</ul><p> </p>")
Next

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

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

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

SuppliersAndProducts.aspx

<%@ Page Language="VB" CodeFile="SuppliersAndProducts.aspx.vb"
    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>
        <h1>
            Suppliers and Their Products</h1>
        <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="<%# CType(CType(Container.DataItem, System.Data.DataRowView).Row, Northwind.SuppliersRow).GetProducts() %>"
                            DataTextField="ProductName">
                            </asp:BulletedList>
                        </ItemTemplate>
                    </asp:TemplateField>
                </Columns>
            </asp:GridView>
             </p>
    </div>
    </form>
</body>
</html>

SuppliersAndProducts.aspx.vb

Imports NorthwindTableAdapters
Partial Class SuppliersAndProducts
    Inherits System.Web.UI.Page
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles Me.Load
        Dim suppliersAdapter As New SuppliersTableAdapter
        GridView1.DataSource = suppliersAdapter.GetSuppliers()
        GridView1.DataBind()
    End Sub
End Class

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

小结

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

快乐编程!





上一页 | 1 | 2 | 3 |下一页

下一篇教程