青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

道。道。道

安全特性不等于安全的特性

   :: 首頁 :: 聯系 :: 聚合  :: 管理

常用鏈接

搜索

  •  

最新評論

導言

  作為web開發人員,我們的生活圍繞著數據操作。我們建立數據庫來存儲數據,寫編碼來訪問和修改數據,設計網頁來采集和匯總數據。本文是研究在ASP.NET 2.0中實現這些常見的數據訪問模式之技術的長篇系列教程的第一篇。我們將從創建一個軟件框架開始,這個框架的組成部分包括一個使用強類型的DataSet的數據訪問層(DAL),一個實施用戶定義的業務規則的業務邏輯層(BLL),以及一個由共享頁面布局的ASP.NET網頁組成的表現層。在打下這個后端的基礎工作之后,我們將開始轉向報表,示范如何顯示,匯總,采集,和驗證web 應用的數據。這些教程旨在簡明扼要,使用了許多屏幕截圖,提供了按步就 班(step-by-step)的指導,帶你經歷這個開發過程。每個教程都有C# 版和VB版,并且附有涉及的完整的編碼的下載。(這第一個教程比較長,但以后其他的教程將以更容易消化的篇幅推出。)

  在這些教程中,我們將使用置于App_Data 目錄內的微 軟SQL Server 2005 Express版的Northwind數據庫。除了數據庫文件外,App_Data目錄還帶有用于創建數據庫的SQL腳本,萬一你想使用別的數據庫版本的話。如果你愿意的話,你也可以直接從微軟下載這些腳本。如果你使用別的SQL Server版本的Northwind數據庫的話,你需要更新Web.config文件中的NORTHWNDConnectionString設置。本教程中的web應用是個基于文件系統的網站項目,是使用Visual Studio 2005 專業版建立起來的。但是,所有的教程都可以在Visual Studio 2005的免費版本Visual Web Developer中運行。

  在這個教程里,我們將從頭開始,先創建一個數據訪問層(DAL),然后在第二個教程里創建一個業務邏輯層(BLL),在第三個教程里設計頁面布局和導航。以后的教程將建立在這三個教程的基礎之上。在第一個教程里,我們要討論的內容多多,所以,請打開Visual Studio,讓我們動起手來!

  第一步:創建一個Web項目,配置數據庫連接

  在我們開始創建數據訪問層(DAL)之前,我們首先需要創建一個網站,以及建立一個數據庫。我們從創建一個基于文件系統的ASP.NET 網站開始。次序如下,打開文件(File)菜單,選擇新的網站 (New Web Site),系統會顯示一個新網站對話框,選擇ASP.NET網站模板(Web Site template),設置定 位(Location)列表的選項為文件系統( File System),然后選這一個放置這個網站的文件夾,然后選擇編程語 言為C#。


圖 1: 創建一個基于文件系統的網站

  Visual Studio會為你生成一個新的網站,同時生成一個名為Default.aspx的網頁,和一 個App_Data文件夾。

  網站生成之后,下一步是在Visual Studio的服務器資源管理器(Server Explorer)里為你的數據庫添加一個引 用(reference)。把一個數據庫添加到服務器資源管理器之后,你就能在Visual Studio環境里添加數據表,存 儲過程,視圖等等。你也能查看數據庫里的數據,手工或用查詢生成器(Query Builder)的圖形界面建立你自己的查詢語句。此外,當我們為DAL創建強類型的DataSet時,我們需要把Visual Studio指向作為DataSet數據源的目標數據庫。雖然我們可以在適當時候提供所昀敳獴搨 ???oЁ涉及的數據庫連接信息,但假如我們預 先在服務器資源管理器里注冊這些數據庫的話,Visual Studio會自動把這些數據庫填充到一個下拉列表中去 。

  把Northwind數據庫添加到服務器資源管理器中去的步驟取決于你想使用放置在App_Data文件夾 里的SQL Server 2005 Express 版本數據庫,還是你想使用已經建立好了的SQL Server 2000或2005 數據庫服 務器。

  使用置于App_Data文件夾中的數據庫

  如果你沒有可連接的SQL Server 2000 或2005服務器,或者你就是想避免給數據庫服務器添加數據庫,你可以使用SQL Server 2005 Express版的Northwind數據庫,該數據庫位于下載源碼中的App_Data文件夾里(NORTHWND.MDF)。

  置于App_Data文件夾里的數據庫會被自動添加到服務器資源管理器中。假設你已經在你的機器上安裝了SQL Server 2005 Express版本,那么你應該在服務器資源管理器中看到一個名為NORTHWND.MDF的節點,你可以將這個節點擴展開來,瀏覽其中的數據表,視圖,存儲過程等等 (參考圖2)。

  App_Data文件夾還可以放置微軟的Access.mdb數據庫文件,跟SQL Server 的數 據庫文件類似,這些Access文件會被自動地添加到服務器資源管理器中。如果你不想用任何SQL Server數據庫,那么你總歸可以下載微軟Access版本的Northwind 數據庫文件,然后將其放置于App_Data文件夾中。但記住,Access數據庫沒有SQL Server那么多功能,而且它并不是設計來在網站情形下使用的。此外,在后面幾個教程里將用到Access數據庫不支持的數據庫層次的功能。

  連接到微軟SQL Server 2000或2005數據庫服務器中的數據庫

  或者,你也可以連接到安裝在數據庫服務器上的Northwind數據庫。假如數據庫服務器上尚未安裝Northwind數據庫的話,你首先必須運行本教程下載文件中的安裝腳本來把數據庫添加到數據庫服務器上去,或者你也可以從微軟網站上直接下載SQL Server 2000的Northwind數據庫以及安裝腳本

  安裝數據庫完畢之后,去Visual Studio中的服務器資源管理器,在數據連接(Data Connections)節點上按右鼠標,選擇“添加連接(Add Connection)”。如果你看不到服務器資源管理器,去菜單“查看(View)”點擊 “服務器資源管理器”,或者按組合鍵Ctrl+Alt+S來打開服務器資源管理器。這會打開添加連接的對話框,在這上面,你可以設置需要連接的服務器,認證信息,以及數據庫名字。在你成功配置數據庫連接信息,按OK按鈕之后,數據庫就會被添加成數據連接節點之下的一個節點。然后,你就可以擴展數據庫節點來瀏覽數據表,視圖,存儲過程等等。


圖 2: 添加一個到你的數據庫服務器上的Northwind數據庫的連接

  第二步:創建一個數據訪問層

  與數據打交道時,一種做法是把跟數據相關的邏輯直接放在表現層中(在一個web應用里,ASP.NET網頁構成了表現層)。其形式一般是在ASP.NET 網頁的編碼部分寫ADO.NET 編碼或者在標識符部 分使用SqlDataSource控件。在這兩種形式里,這種做法都把數據訪問邏輯與表現層緊密耦合起來了。但推薦 的做法是,把數據訪問邏輯從表現層分離開來。這個分開的層被稱作是數據訪問層,簡寫為DAL,一般是通過 一個單獨的類庫項目來實現的。這種分層框架的好處在很多文獻里都有闡述(詳見本教程最后的“附加讀物”里 的資源),在本系列中我們將采用這種方法。

  跟底層數據源相關的所有編碼,譬如建立到數據庫的連接,發出SELECT,INSERT ,UPDATE,和DELETE命令等的編碼,都應該放置在DAL中。表現層不應該包含對 這些數據訪問編碼的任何引用,而應該調用DAL中的編碼來作所有的數據訪問請求。數據訪問層包含訪問底層數據庫數據的方法。譬如,Northwind數據庫中,有Products和Categories兩個表,它們記錄了可供銷售的產品以及這些產品 所屬的分類。在我們的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

  要返回強類型對象,開發人員可以創建自定義業務對象,或者使用強類型的DataSet。開發人員實現的業務對 象類,其屬性往往是對相應的底層數據表的字段的映射。而一個強類型的DataSet,則是Visual Studio基于數 據庫schema為你生成的一個類,其成員的類型都是由這個schema決定的。強類型的DataSet本身,是由繼承 于ADO.NET中DataSet,DataTable,和DataRow類的子類組成的。除了強類型的DataTable外,強類型的DataSet現在還包括TableAdapter類,這些類包含了填充DataSet中的DataTable和把 DataTable的改動傳回數據庫的各種方法。

  在這些教程的架構里,我們將使用強類型的DataSet。圖3示范說明了使用強類型的DataSet之應用程序的不 同層間的流程(workflow)。


圖 3: 把所有的數據訪問編碼委托給DAL

  創建強類型的DataSet和Table Adapter

  我們開始創建我們的DAL,先給我們的項目添加一個強類型的DataSet。做法如下,在昀敳獴搨 ???oЁ解決方案管理器里的項目 節點上按右鼠標,選擇“添加新項(Add a New Item)”。在模板列單里選擇DataSet,將其命名 為Northwind.xsd。


圖 4: 給你的項目添加一個新的DataSet

  在點擊“添加(Add)”按鈕后,Visual Studio會問我們是否將DataSet添加到App_Code文件夾中,選擇“Yes” 。然后Visual Studio會顯示強類型的DataSet的設計器,同時會啟動TableAdapter配置向導,允許你給你的強 類型DataSet添加第一個TableAdapter。

  強類型的DataSet 起了強類型對象的集合的作用,它由強類型DataTable實例組成,每個強類型DataTable又進 而由強類型的DataRow實例組成。我們將為這個教程系列要用到的每個數據表建立一個對應的強類型DataTable 。讓我們開始吧,先為Products表建立一個DataTable。

  記住,強類型的DataTable并不包括如何訪問對應底層的數據表的任何信息。要獲取用來填充DataTable的數據 ,我們使用TableAdapter類,它提供了數據訪問層的功能。對于我們的Products DataTable, 相應的TableAdapter 類將包 括GetProducts()和GetProductByCategoryID(categoryID)等方法,而我 們將在表現層調用這些方法。DataTable的作用是在分層間傳輸數據。

  TableAdapter配置向導首先要你選擇使用哪個數據庫。下拉框里列出了服務器資源管理器內的那些數據庫。如 果你預先沒有把Northwind數據庫添加到服務器資源管理器里去的話,這時你可以點擊新連接按鈕來添加。


圖 5: 在下拉框里選擇Northwind數據庫

  選擇好數據庫后,按“下一步”按鈕,向導會問你是否想在Web.config文件里存放連接字符串。 將連接字符串存放在Web.config文件里,你可以避免把連接字符串硬寫在TableAdapter類的編 碼中,如果將來連接字符串信息改動的話,這種做法會極大地簡化要做的編碼改動。如果你選擇在配置文件存 放連接字符串,連接字符串將被置放于<connectionStrings>段落中,這個段落可以被加密來提高安全,也可以通過IIS 圖形界面管理工具中的新的ASP.NET 2.0屬性頁來修改。當然這個工具更適于管理員。


圖6: 在Web.config中存放連接字符串

  接下來,我們需要定義第一個強類型的DataTable的schema,同時為用來填充強類型DataSet的TableAdapter類 提供第一個方法。這兩步可以通過建立一個返回對應于DataTable的數據表的字段的查詢同時完成。在向導的 最后,我們將為這個查詢對應的方法命名。完成后,這個方法可以在表現層調用,它會執行設置好的查詢,進 而填充一個強類型的DataTable。

  開始定義SQL查詢之前,我們必須首先選擇我們想要TableAdapter執行查詢的方式。我們可以直接用ad-hoc的SQL語句,或建立一個新的存儲過程,或使用現存的存儲過程。在這些教程里,我們將使用ad-hoc的SQL語句。


圖 7: 用SQL語句查詢數據

  至此,我們可以手工輸入SQL查詢。當生成TableAdapter的第一個方法時,你一般想要讓你的查詢返回那些需 要在對應的DataTable中存放的字段。我們可以建立一個從Products表里返回所有字段,所有數 據行的查詢來達到我們的目的:


圖 8: 在文本框里輸入SQL查詢

  或者,我們可以使用查詢生成器(Query Builder),用圖形界面來構造查詢,如圖9所示。


圖 9: 通過查詢編輯器生成查詢

  在生成查詢之后,在移到下一屏之前,點擊“高級選項(Advanced Options)”按鈕。在網站項目里,在默認 情形下,“生成插入,更新,刪除語句”是唯一已被選中的選項。如果你在類庫項目或Windows項目里運行這個 向導的話,“采用優化的并發控制(optimistic concurrency)”選項也會被選中。現在先別選“采用優化的并發 控制”這個選項。在以后的教程里我們會詳細討論優化的并發控制。


圖 10: 只選“生成插入,更新和刪除語句”這個選項

  在核實高級選項后,按“下一步(Next)”按鈕轉到最后一屏。在這里,配置向導會問我們要給TableAdapter選擇添加什么方法。填充數據有兩種模式:
  • 填充DataTable – 這個做法會生成一個方法,該方法接受一個DataTable的參數,基于查詢的結果 填充這個DataTable。譬如,ADO.NET的DataAdapter類就是在它的Fill()方法中實現這個模式的 。
  • 返回DataTable – 這個做法會生成一個方法,該方法會創建并填充一個DataTable,然后將 其作為方法的返回值。

  你可以讓TableAdapter實現其中一個模式或者同時實現兩個模式。你也可以重新命名這里提供的這些方法。讓 我們對兩個復選框的選項不做改動,雖然我們在這些教程里只需要使用后面這個模式。同時,讓我們把那個很 一般性的GetData方法名改成GetProducts。

  這最后一個復選框,“生成DB直接方法(GenerateDBDirectMethods)”,如果選了的話,會為TableAdapter自動生 成Insert(),Update(),和Delete()方法。如果你不選這個選項 的話,所有的更新都需要通過TableAdapter唯一的Update()方法來實現,該方法接受一個強類型的DataSet,或者一個DataTable,或者單個DataRow,或者一個DataRow數組。(假如你 在圖9所示的高級屬性里把“生成添加,更新和刪除語句”的選項去掉的話,這個復選框是不起作用的)。讓我們 保留這個復選框的選項。


圖 11: 把方法名字從 GetData 改成 GetProducts

  按“完成”按鈕結束向導。在向導關閉后,我們回到DataSet設計器中,它會顯示我們剛創建的DataTable。你可 以看到Products DataTable的字段列單(ProductID, ProductName 等),還有ProductsTableAdapter的Fill()和GetProducts()方法 。


圖 12: Products DataTable和ProductsTableAdapter被添加到強類 型DataSet中

  至此,我們生成了含有單一DataTable類(Northwind.Products)的強類型DataSet以及一個含 有GetProducts()方法的強類 型DataAdapter類(NorthwindTableAdapters.ProductsTableAdapter)。通過這些對象可以用下 列編碼來獲取所有產品的列單:

C#
1
            2
            3
            4
            5
            6
            7
            
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

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
            
<%@ 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>
            <h1>
            All Products</h1>
            <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

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
            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的話,我們一行編碼都不 用寫,而且還能得到分頁和排序支持呢!

  第三步:給數據訪問層添加參數化的方法

  至此,ProductsTableAdapter只有一個方法,GetProducts(),它返回數據庫里的所有產品。能夠操作所有的產品當然有用,但很多時候我們想要獲取關于一個指定產品的信息,或者屬于某個特 定分類的所有產品。要想給我們的數據訪問層添加這樣的功能,我們可以給TableAdapter添加參數化的方法。

  讓我們來添加一個GetProductsByCategoryID(categoryID)方法。為給DAL添加新的 方法,讓我們回到DataSet設計器,在ProductsTableAdapter上按右鼠標,然后選擇“添加查 詢(Add Query)”。


圖 14: 在TableAdapter上按右鼠標,選擇“添加查詢”

  向導首先會問我們是否要通過一個ad-hoc SQL語句還是生成一個新存儲過程或者使用現有存儲過程來訪問 數據庫。讓我們還是選擇使用SQL 語句。接著,向導會問我們使用什么類型的SQL查詢。因為我們想返回屬于 指定分類的所有產品,我們需要寫一個返回數據行的SELECT語句。


圖 15: 選擇生成一個返回數據行的SELECT語句

  下一步是定義用于訪問數據的SQL查詢語句。因為我們只想返回屬于指定分類的那些產品,我重 用GetProducts()里的SELECT語句,但添加了一個WHERE 子 句:WHERE CategoryID = @CategoryID。其中的@CategoryID參數 向TableAdapter配置向導表示我們正在生成的方法將需要一個對應類(即,可為null-nullable的整數)的輸入 參數。


圖 16: 輸入一個只返回指定分類的產品的查詢

  在最后一步,我們可以選擇使用何種數據訪問模式,還可以定制生成的方法的名字。對應于Fill 模式,讓我們把名字改成FillByCategoryID,對返回DataTable模式的方法(GetX方法),讓我們來用GetProductsByCategoryID這個名字。


圖 17: 為TableAdapter的方法選擇名字

  在結束向導后,DataSet設計器包含了這些新的TableAdapter的方法。


圖18: 通過分類來查詢產品

  花點時間用同樣的手法添加一個GetProductByProductID(productID) 方法。

  這些參數化的查詢可以在DataSet設計器里直接測試。在TableAdapter中的方法上按右鼠標,然后選擇“預 覽數據(Preview Data)”。接著,輸入對應參數的值,然后按“預覽(Preview)”。


圖19: 屬于飲料(Beverages)類的那些產品列單

  通過我們的DAL中的GetProductsByCategoryID(categoryID)方法,我們就能設計一 個ASP.NET網頁來顯示屬于指定分類的那些產品。下面這個例子顯示了屬于Beverages(飲 料)類(CategoryID=1)的所有產品。

  Beverages.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
            
<%@ 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>
            <h1>Beverages</h1>
            <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

C#
1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            
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: 屬于Beverages(飲料)類的所有產品顯示

  第四步:插入,更新和刪除數據

  常用的插入,更新和刪除數據的模式有兩種。第一種模式,我稱之為DB直接模式,涉及的方法被調用時,會向數據庫里發出一個INSERT, 或UPDATE,或DELETE命令,這個命令只對單個數據庫記錄做操作。象這樣的方法一般接受一系列對應于插入,更新或刪除的值的標量參數(譬如整數,字符串,布爾值,日期時間等)。譬如,用這個模式來操作Products表的話,刪除方法會接受一個整數參數,代表所需要刪除的記錄的ProductID,而插入方法則會接受一個對應于ProductName的字符串,對應 于UnitPrice的decimal值,對應于UnitsOnStock的整數等等。


圖 21: 每個插入,更新,和刪除請求都被立刻發送到數據庫

  另外一個模式,我稱之為批更新模式,可以在一個方法調用里更新整個DataSet,或者整個DataTable,或 者一個DataRow集合。在這個模式里,開發人員在一個DataTable中刪除,插入,修改DataRow,然后把這 些DataRow或整個DataTable傳給一個更新方法。然后這個方法會輪循傳入的DataRow們,通過DataRow的RowState屬 性屬性來決定這些DataRow是否被改動過,或是新記錄,或是被刪除的記錄,然后為每個記錄發出合適的 數據庫命令。


圖 22: 在Update 方法調用之后,所有的變動都與數據庫同步了

  在默認情形下,TableAdapter采用批更新模式,但也支持DB直接模式。因為我們在創建我們的TableAdapter時的高級選項中選擇了“生成插入,更新,和刪除語句” 這個選項,ProductsTableAdapter 包含了一個 Update()方法,該方法實現了批 更新模式。具體地說,TableAdapter包含了一個Update()<昀敳獴搨 ???oЁCODE> 方法,可以傳入一個強類型 的DataSet,或者一個強類型的DataTable,或者一個和多個DataRow。假如你在一開始創建TableAdapter時的選項中沒有清除“生成DB直接方法(GenerateDBDirectMethods)”復選框的話,DB直接模 式也會通過Insert(),Update()和Delete()方法來實現。

  這兩種數據修改模式都使用 了TableAdapter的InsertCommand,UpdateCommand, 和DeleteCommand屬性來向數據庫發出對應 的INSERT,UPDATE和DELETE命令。你可以在DataSet設計器里點擊TableAdapter,然后在屬性窗口查看和改 動InsertCommand,UpdateCommand, 和DeleteCommand屬性。(確 認你選擇了TableAdapter,并且ProductsTableAdapter對象是屬性窗口中下拉框里被選中的項)


圖23: TableAdapter包含InsertCommand,UpdateCommand, 和DeleteCommand等屬性

  想查看或改動這些數據庫命令的屬性的話,點擊CommandText子屬性,這會啟動對應的查詢 生成器。


圖 24: 在查詢生成器里配置插入,更新,刪除語句

  下面的編碼例子示范了如何使用批更新模式來把沒被終止的,且庫存等于或少于25個單元的產品的價格加 倍:

C#
1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            
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);
            

  下面的編碼示范如何使用DB直接模式刪除一個產品,更新一個產品,然后添加一個新的產品:

C#
1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            
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);
            
  創建自定義的插入,更新,刪除方法

  用DB直接法生成的Insert(), Update(),和Delete()方法有時 候會感覺有點不方便,特別是當數據表有許多字段的時候。看一下前面這個編碼例子,沒有IntelliSense的幫 助的話,不是很清楚Products表的哪個字段對 應Update()和Insert()方法中的哪個輸入參數。有時候我們只要更新一到二個字 段或者需要一個自定義的Insert()方法,這個方法需要返回剛插入的記錄 的IDENTITY(自增)的字段值。

  要創建這樣的自定義方法,回到DataSet設計器。在TableAdapter上按右鼠標,選擇“添加查詢”,然后回 到TableAdapter配置向導。在第二屏上,我們可以指明要生成的查詢的類型。讓我們生成一個添加新 的product(產品)記錄,然后返回新添加記錄的ProductID值的方法。因此,選擇生成一個插 入(INSERT)型查詢。


圖25: 創建一個給Products表添加新記錄的方法

  下一個屏顯示InsertCommand的CommandText屬性。在查詢語句后面,增添一 個SELECT SCOPE_IDENTITY()的查詢,這查詢將返回當前同一個操作范圍內插 入IDENTITY字段的最后那個identity 值。(詳見技術文檔中關 于SCOPE_IDENTITY()的內容以及為什么你應該使用SCOPE_IDENTITY()而不是 @@IDENTITY)。確認在添加SELECT語句前,你在INSERT語句后面添一個分號 。


圖26: 增添查詢返回SCOPE_IDENTITY()值

  最后,把這個新方法命名為InsertProduct。


圖 27:放方法名字設成InsertProduct

  當你返回DataSet設計器時,你將看到ProductsTableAdapter多了一個新的方 法,InsertProduct。如果對應Products表的每個字段,這個新的方法沒有對應的參數的話,非常可能的原因是,你忘了給INSERT語句的結尾添加一個分號(semi-colon)。重新配 置InsertProduct方法,確認在INSERT和SELECT語句間有個分號。

  在默認情形下,插入方法調用的是非查詢(non-query)方法,意即,他們只返回受影響的記錄數。但是,我們想要讓InsertProduct方法返回一個查詢返回的值,而不是受影響的記錄數。這可以把InsertProduct方法的ExecuteMode屬性改 成Scalar(標量)來實現。


圖 28:把ExecuteMode屬性改成Scalar

  下面的編碼示范如何使用這個新的InsertProduct方法:

C#
1
            2
            3
            4
            5
            6
            7
            
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);
            
  第五步:完成數據訪問層

  注意,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>&nbsp;</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>
            &nbsp;</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基礎之上。在下一個教程里,我們將定義一堆業務規則,然后看一下如何在一個分開的 業務邏輯層里實現這些規則。
posted on 2007-11-15 20:37 獨孤九劍 閱讀(159) 評論(0)  編輯 收藏 引用 所屬分類: Learn articles
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            在线看日韩av| 久久gogo国模啪啪人体图| 亚洲国产欧美不卡在线观看| **性色生活片久久毛片| 久久久99精品免费观看不卡| 六月婷婷一区| 亚洲激情视频在线播放| 在线亚洲国产精品网站| 亚洲自拍另类| 免费在线欧美视频| 欧美天天影院| 激情欧美一区二区三区在线观看| 亚洲精品国产无天堂网2021| 亚洲午夜精品久久| 免费成人高清在线视频| 亚洲在线中文字幕| 国产午夜精品在线| 一区二区欧美在线观看| 久久一二三国产| 亚洲欧美不卡| 一区二区在线不卡| 亚洲精品在线视频观看| 欧美一级在线播放| 国产精品都在这里| 亚洲人体1000| 欧美电影免费观看高清完整版| 一区二区三区国产在线观看| 蜜臀久久99精品久久久画质超高清| 国产精品xnxxcom| 9人人澡人人爽人人精品| 久久久噜噜噜久久人人看| 99精品欧美一区| 欧美高清视频在线播放| 一色屋精品亚洲香蕉网站| 久久精品99无色码中文字幕| 亚洲一区二区三区影院| 国产精品嫩草久久久久| 欧美日韩成人激情| 欧美午夜精品电影| 久久久久久网址| 欧美精品久久99| 日韩一级精品| 欧美主播一区二区三区美女 久久精品人 | 噜噜噜噜噜久久久久久91| 亚洲一区三区电影在线观看| 亚洲欧洲精品一区二区三区波多野1战4| 欧美午夜理伦三级在线观看| 欧美成人视屏| 国产一区二区三区在线观看网站| 欧美一区二区三区四区视频| 日韩一区二区精品葵司在线| 欧美日韩一区二区三区视频| 洋洋av久久久久久久一区| 欧美资源在线观看| 欧美一区=区| 国产精品成人一区| 亚洲美女色禁图| 日韩亚洲在线观看| 奶水喷射视频一区| 卡通动漫国产精品| 精品69视频一区二区三区| 午夜精品一区二区三区电影天堂 | 久久久不卡网国产精品一区| 国产欧美日韩| 美女国产一区| 樱桃国产成人精品视频| 久久国产精品一区二区三区四区| 亚洲国产日韩一区| 99精品视频免费在线观看| 亚洲日韩欧美视频一区| 老司机67194精品线观看| 99精品热视频| 欧美日韩国产首页| 一区二区日韩| 亚洲欧美卡通另类91av| 欧美一区二区女人| 久久久欧美精品| 在线播放日韩专区| 美女免费视频一区| 91久久国产精品91久久性色| 亚洲精品1区2区| 欧美日本亚洲韩国国产| 久久久久看片| 亚洲国产导航| 久久精品电影| 亚洲国产精品传媒在线观看| 9i看片成人免费高清| 欧美日在线观看| 亚洲欧美综合网| 亚洲自拍啪啪| 韩国美女久久| 99亚洲精品| 久久久久**毛片大全| 亚洲国产精品成人| 欧美日韩三级| 欧美中文字幕| 亚洲国产高清在线观看视频| 国产精品网站一区| 亚洲美女福利视频网站| 午夜精品福利视频| 欧美日韩精品一区二区三区| 亚洲一级黄色片| 午夜国产精品视频| 在线观看欧美成人| 欧美日韩一区二区三区视频| 欧美一区二区三区在线播放| 亚洲国产精品欧美一二99| 亚洲午夜电影在线观看| 精品成人一区二区三区| 欧美视频观看一区| 久久久久久成人| 蜜桃av综合| 亚洲欧美日韩在线播放| 亚洲国产日韩欧美在线99| 国产精品免费电影| 欧美不卡视频一区| 欧美激情亚洲另类| 亚洲国产99| 国产视频自拍一区| 欧美日韩中文字幕在线| 免费看精品久久片| 欧美一区午夜视频在线观看| 亚洲免费观看高清完整版在线观看| 久热精品视频在线免费观看| 午夜精品一区二区三区在线视| 亚洲人成亚洲人成在线观看| 在线播放不卡| 国产在线欧美| 欧美成人免费视频| 久久黄色小说| 欧美一区二区观看视频| 一区二区高清视频在线观看| 91久久精品国产91久久| 快射av在线播放一区| 久久久久国色av免费看影院| 午夜精品国产| 亚洲欧美日韩另类| 亚洲欧美日韩精品| 亚洲永久免费观看| 亚洲午夜在线观看视频在线| 一区二区高清视频| 一本一道久久综合狠狠老精东影业| 亚洲伦理自拍| 国产女精品视频网站免费| 欧美特黄一区| 国产精品久久久久久久久久久久 | 宅男噜噜噜66一区二区| 亚洲人午夜精品免费| 亚洲国产精品一区二区www| 亚洲丰满在线| 亚洲日本中文字幕免费在线不卡| 亚洲国产欧美一区二区三区同亚洲| 伊大人香蕉综合8在线视| 亚洲第一天堂av| 亚洲激情av| 日韩视频一区二区三区| 一区二区高清在线| 先锋影音久久久| 亚洲日本理论电影| 99riav国产精品| 亚洲影视在线| 久久国产精品久久久久久久久久| 久久久精品网| 亚洲高清激情| 亚洲一级免费视频| 久久久久久久一区二区| 欧美精品一区三区在线观看| 欧美日韩国产在线播放网站| 国产精品极品美女粉嫩高清在线| 国产欧美一级| 亚洲缚视频在线观看| 一区二区三区免费在线观看| 午夜免费久久久久| 裸体女人亚洲精品一区| 亚洲欧洲综合另类| 久久精品一二三| 亚洲国产欧美日韩精品| 一本大道久久a久久精二百| 午夜在线不卡| 欧美黄网免费在线观看| 国产精品免费视频观看| 亚洲成人在线视频网站| 亚洲视频你懂的| 亚洲美女视频| 久久av一区二区| 亚洲精品一二三| 久久精品综合网| 国产精品成人观看视频免费| 精品动漫3d一区二区三区| 亚洲视频网站在线观看| 美女被久久久| 午夜日韩视频| 国产精品福利在线| 亚洲第一区色| 久久国产欧美| 亚洲网站在线| 欧美精品一区二| 亚洲第一精品福利| 久久国产夜色精品鲁鲁99| 亚洲美女中出|