ASP.NET2.0数据操作之创建数据访问层(4)_[Asp.Net教程] 					第五步:完成数据访问层   注意,ProductsTableAdapters类从Products表中返回的 是CategoryID和SupplierID的值,但并不包括Categories表 的CategoryName字段和Suppliers表的CompanyName字段,尽管当 我们显示产品信息时,这些很可能是我们想要显示的字段。我们可以扩充TableAdapter的起始方 法GetProducts()来包含CategoryName和CompanyName字段的值, 这方法进而会更新强类型的DataTable来包括这些新的字段。    但这会造成一个问题,因为TableAdapter的插入,更新,删除数据的方法是基于这个起始方法的,幸运的是, 自动生成的插入,更新,删除方法并不会受SELECT子句中的子查询的影响。如果我们注意把 对Categories和Suppliers的查询添加成子查询,而不是用JOIN语 句的话,我们可以避免重做这些修改数据的方法。在ProductsTableAdapter中的GetProducts()方法上按右鼠标,选择“配置”,然后,把SELECT子句改成: SQL 1  2  3  4  5  6  7 
  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将包含2个新字段,CategoryName和SupplierName。 
 
   图30: Products DataTable多了2个新字段
    花点时间把GetProductsByCategoryID(categoryID)方法中的SELECT 子句也更新一下。 
    如果你使用JOIN句法更新GetProducts()中的SELECT语句的话 ,DataSet设计器不能使用DB直接模式自动生成插入,更新,以及删除数据库记录的方法。你必须手工生成这 些方法,就象本教程早先时候我们对InsertProduct方法的做法一样。此外,你必须手工提供 InsertCommand,UpdateCommand和DeleteCommand属性值,假如你 想使用批更新模式的话。
   添加其他的TableAdapter 
    到目前为止,我们只讨论了针对单个数据表的单个TableAdapter。但是,Northwind数据库里含有我们需要在 我们的web应用中使用的几个相关的表。一个强类型的DataSet可以包含多个相关的DataTable。因此,为了完 成我们的DAL,我们需要为这些我们将来要用到的数据表添加相应的DataTable。步骤如下,打开 DataSet设计 器,在设计器上按右鼠标,选择“添加/TableAdapter”。这会生成一个新的DataTable和TableAdapter,然后我 们早先讨论过的配置向导会指引你完成配置。
    花上几分钟,创建对应于下列查询的TableAdapter及其方法。注意,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.ProductID) 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.ProductID) 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.ProductID)  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.ProductID) 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 ployeeID, LastName, FirstName,  Title, HireDate, ReportsTo, Country FROM Employees WHERE EmployeeID = @EmployeeID
  
  
 
   图31:添加了四个TableAdapter后的DataSet设计器
   给DAL添加定制编码 
    添加到强类型DataSet中的TableAdapter和DataTable是在一个XML Schema定义文 件(Northwind.xsd)中定义的。你可以在解决方案资源管理器里在Northwind.xsd 文件上按右鼠标,选择“查看编码(View Code)”,打开这个Schema文件来查看其中内容。
 
   图32:Northwinds强类型DataSet的XML Schema定义文件
    这个schema信息在设计时编译之后会被翻译成C#或Visual Basic 编码,或者如果有必要的话,会在运行时 翻译,然后你就能在调试器里单步遍历执行。想查看这些自动生成的编码的话,在类视图里,展 开TableAdapter 类或者强类型的DataSet 类。如果在屏幕上看不到类视图的话,在“查看”(View)菜单里选择“ 类视图”,或者按键组合Ctrl+Shift+C。在类视图里,你能看到强类型的DataSet类和TableAdapter类的属性,方法和事件。想看某个特定的方法的编码话,在类视图双击对应方法的名字或者在方法上按右鼠标,选 择“移至定义区(Go To Definition)”。 
 
   图33:在类视图里选择“移至定义区(Go To Definition)”,查看自动生成的编码
    虽然自动生成的编码省时省力,但这样的编码往往是非常通用化的(generic),为满足一个应用程序特有的需 求需要做些定制。但扩展自动生成的编码的风险在于,如果生成这些编码的工具决定该是重新生成这些编码的 时候了,则会把你定制的编码冲掉。使用.NET 2.0中的一个新的部分(partial)类的概念,很容易将一个类的 定义分写在几个文件里。这允许我们给自动生成的类添加我们自己的方法,属性,和事件,而不用担心Visual Studio会冲掉我们的定制编码。 
    为示范如何定制DAL起见,让我们来给SuppliersRow 添加一个GetProducts()方法。这 个SuppliersRow类代表了Suppliers表的个别记录,每个供应商(supplier)可以 提供0个到多个产品,所以GetProducts()将返回指定的供应商的这些产品。做法如 下,在App_Code文件夹里添加一个新的类文件,将其命名为SuppliersRow.cs, 然后在其中添加下列编码: 
 
  C# 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17 
  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);  }  }  } 
     这个部分(partial)类指示编译器在编译Northwind.SuppliersRow类时,应该包含我们刚定义的这个GetProducts()方法。如果你编译你的项目,然后返回类视图,你就会看到GetProducts()已被列为Northwind.SuppliersRow的一个方法。 
 
   图34: GetProducts()方法成为Northwind.SuppliersRow类的一部 分
    GetProducts()方法现在就能用来枚举一个指定供应商的产品列单,如下列编码所示:
 
 
 C# 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19 
  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>");  } 
     This data can also be displayed in any of ASP.NET's data Web controls. The following page uses a GridView control with two fields:数据也可以在任何一种ASP.NET的Web控件中显示。下面这个网页 使用了含有2个字段的GridView 控件:
 
 
 - 一个BoundField用以显示每个供应商的名字, 
  - 另一个TemplateField,包含了一个BulletedList控件,用来绑定针对每个供应商调用 的GetProducts()方法返回的结果 
 
   我们将在以后的教程里讨论怎样来显示这样的主/从(master-detail)报表。在这里,这个例子的目的是用 来示范如何使用添加到Northwind.SuppliersRow类中的自定义的方法的。
  SuppliersAndProducts.aspx
  ASP.NET 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41 
  <%@ Page Language="C#"  AutoEventWireup="true" CodeFile="SuppliersAndProducts.aspx.cs"  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="<%#  ((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
 
 C# 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22 
  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: 供应商的公司名字列在左栏,他们的产品列在右栏
    总结 
    构造web应用时,创建DAL应该是你最先做的步骤之一,应该在你开始创建表现层之前进行。使用Visual Studio的话,创建基于强类型DataSet的DAL是个可以不写一行编码,在10到15分钟内就可完成的任务。以后的 教程将建立在这个DAL基础之上。在下一个教程里,我们将定义一堆业务规则,然后看一下如何在一个分开的 业务逻辑层里实现这些规则。 			
 
 |