一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

服務(wù)器之家 - 編程語(yǔ)言 - ASP.NET教程 - 在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程

2020-01-10 15:42heker2007 ASP.NET教程

本文主要講解使用TableAdapter設(shè)置向?qū)ё詣?dòng)創(chuàng)建增刪改查的存儲(chǔ)過程,雖然自動(dòng)創(chuàng)建存儲(chǔ)過程可以節(jié)省時(shí)間,但他們會(huì)包含一些無(wú)用的參數(shù),下節(jié)我們會(huì)介紹TableAdapter使用現(xiàn)有的存儲(chǔ)過程。

導(dǎo)言:

  本教程的Data Access Layer (DAL)使用的是類型化的數(shù)據(jù)集(Typed DataSets).就像我們?cè)诘谝徽隆秳?chuàng)建一個(gè)數(shù)據(jù)訪問層》里探討的一樣,該類型化的數(shù)據(jù)集由強(qiáng)類型的DataTable和TableAdapter構(gòu)成。DataTable描繪的是系統(tǒng)里的邏輯實(shí)體而TableAdapter引用相關(guān)數(shù)據(jù)庫(kù)執(zhí)行數(shù)據(jù)訪問,包括對(duì)DataTable填充數(shù)據(jù)、執(zhí)行返回標(biāo)量數(shù)據(jù)(scalar data)的請(qǐng)求、添加,更新,刪除數(shù)據(jù)庫(kù)里的記錄等.

  TableAdapter執(zhí)行的SQL命令要么是某個(gè)特定的SQL statements,比如SELECT columnList FROM TableName;要么是存儲(chǔ)過程.本教程前面部分的TableAdapter使用的是SQL statements.不過很多開發(fā)者和數(shù)據(jù)庫(kù)管理員基于安全、便于維護(hù)等方面的考慮,偏愛使用存儲(chǔ)過程;不過也有的人出于靈活性的考慮偏愛使用SQL statement.就我自己而言,我也偏向于存儲(chǔ)過程.在前面的文章,出于簡(jiǎn)化的目的我選用的是SQL statements.

  當(dāng)定義一個(gè)新TableAdapter或添加新方法時(shí),使用TableAdapter的設(shè)置向?qū)В覀兛梢院苋菀椎膭?chuàng)建新的或使用現(xiàn)有的存儲(chǔ)過程.在本文,我們將考察如何使用設(shè)置向?qū)ё詣?dòng)的生產(chǎn)存儲(chǔ)過程。在下一章我們考察如何設(shè)置TableAdapter的方法使用現(xiàn)有的或手動(dòng)創(chuàng)建存儲(chǔ)過程.

  注意:關(guān)于討論到底使用存儲(chǔ)過程還是使用SQL statements的問題,可參考Rob Howard的博客文章《Don't Use Stored Procedures Yet?》(http://weblogs.asp.net/rhoward/archive/2003/11/17/38095.aspx)和Frans Bouma的博客文章《Stored Procedures are Bad, M'Kay?》(http://weblogs.asp.net/fboue/2003/11/18/38178.aspx

存儲(chǔ)過程基礎(chǔ)

  一個(gè)存儲(chǔ)過程由一系列的T-SQL statement組成,當(dāng)調(diào)用該存儲(chǔ)過程時(shí)就執(zhí)行這些T-SQL statement.存儲(chǔ)過程可以接受0到多個(gè)輸入?yún)?shù),返回標(biāo)量值、輸出參數(shù),或最常見的返回SELECT查詢值.

  注意:存儲(chǔ)過程Stored procedures也經(jīng)常引用為“sprocs” or “SPs”.

  可以使用T-SQL statement語(yǔ)句CREATE PROCEDURE來(lái)創(chuàng)建存儲(chǔ)過程.比如下面的T-SQL腳本創(chuàng)建了一個(gè)名為GetProductsByCategoryID的存儲(chǔ)過程,它有一個(gè)名為 @CategoryID的參數(shù),并且將表Products里與CategoryID值相吻合的那條記錄的ProductID, ProductName, UnitPrice,以及Discontinued值返回.

?
1
2
3
4
5
6
7
8
9
CREATE PROCEDURE GetProductsByCategoryID
(
 @CategoryID int
)
AS
 
SELECT ProductID, ProductName, UnitPrice, Discontinued
FROM Products
WHERE CategoryID = @CategoryID

創(chuàng)建后,我們可以用下面的代碼調(diào)用它:

?
1
EXEC GetProductsByCategory categoryID

  注意:在下篇文章我們將在Visual Studio IDE集成環(huán)境里創(chuàng)建存儲(chǔ)過程.不過在本文,我們將用TableAdapter向?qū)?lái)自動(dòng)創(chuàng)建存儲(chǔ)過程.

  除了返回?cái)?shù)據(jù)外,我們還可以在一個(gè)事務(wù)里用存儲(chǔ)過程執(zhí)行多條數(shù)據(jù)庫(kù)命令.比如,假如有一個(gè)名為DeleteCategory的存儲(chǔ)過程,其包含一個(gè)輸入?yún)?shù)@CategoryID,并執(zhí)行2個(gè)DELETE statemets,第一個(gè)是刪除相關(guān)的products,第二個(gè)是刪除category。存儲(chǔ)過程里面的多個(gè)statements并不是自動(dòng)的封裝在一個(gè)事務(wù)里的.我們應(yīng)添加額外的T-SQL commands以確保存儲(chǔ)過程里的多條數(shù)據(jù)庫(kù)命令當(dāng)成原子操作處理.我們將在后面的內(nèi)容考察如何用事務(wù)來(lái)封裝存儲(chǔ)過程的命令.

  當(dāng)在體系的某個(gè)層使用存儲(chǔ)過程時(shí),Data Access Layer的方法將調(diào)用某個(gè)具體的存儲(chǔ)過程而不是發(fā)出一個(gè)SQL statement命令.這樣一來(lái)我們可以發(fā)現(xiàn)、分析發(fā)出的查詢命令.并可以更清楚的看到數(shù)據(jù)庫(kù)是如何使用的.有關(guān)存儲(chǔ)過程基本原理的更多信息,可參考本文結(jié)束部分的延伸閱讀.

第一步:創(chuàng)建數(shù)據(jù)訪問層高級(jí)場(chǎng)景的Web頁(yè)面

在開始之前,讓我們花點(diǎn)時(shí)間創(chuàng)建本文及后面幾篇文章要用到的頁(yè)面。新建一個(gè)名為AdvancedDAL的文件夾,然后添加如下的ASP.NET頁(yè)面,記得使用母版頁(yè)Site.master:

Default.aspx
NewSprocs.aspx
ExistingSprocs.aspx
JOINs.aspx
AddingColumns.aspx
ComputedColumns.aspx
EncryptingConfigSections.aspx
ManagedFunctionsAndSprocs.aspx

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖1:添加相關(guān)的頁(yè)面

像其它文件夾一樣,Default.aspx頁(yè)面將列出本部分的內(nèi)容,記得SectionLevelTutorialListing.ascx用戶控件提供了該功能。因此,將其從解決資源管理器里拖放到Default.aspx頁(yè)面.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖2:將SectionLevelTutorialListing.ascx用戶控件拖到Default.aspx頁(yè)面

最后,將這些頁(yè)面添加到Web.sitemap文件里。特別的,把下面的代碼放在“Working with Batched Data”

<siteMapNode>標(biāo)簽后面:

?
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
<siteMapNode url="~/AdvancedDAL/Default.aspx"
 title="Advanced DAL Scenarios"
 description="Explore a number of advanced Data Access Layer scenarios.">
 
 <siteMapNode url="~/AdvancedDAL/NewSprocs.aspx"
 title="Creating New Stored Procedures for TableAdapters"
 description="Learn how to have the TableAdapter wizard automatically
 create and use stored procedures." />
 <siteMapNode url="~/AdvancedDAL/ExistingSprocs.aspx"
 title="Using Existing Stored Procedures for TableAdapters"
 description="See how to plug existing stored procedures into a
 TableAdapter." />
 <siteMapNode url="~/AdvancedDAL/JOINs.aspx"
 title="Returning Data Using JOINs"
 description="Learn how to augment your DataTables to work with data
 returned from multiple tables via a JOIN query." />
 <siteMapNode url="~/AdvancedDAL/AddingColumns.aspx"
 title="Adding DataColumns to a DataTable"
 description="Master adding new columns to an existing DataTable." />
 <siteMapNode url="~/AdvancedDAL/ComputedColumns.aspx"
 title="Working with Computed Columns"
 description="Explore how to work with computed columns when using
 Typed DataSets." />
 <siteMapNode url="~/AdvancedDAL/EncryptingConfigSections.aspx"
 title="Protected Connection Strings in Web.config"
 description="Protect your connection string information in
 Web.config using encryption." />
 <siteMapNode url="~/AdvancedDAL/ManagedFunctionsAndSprocs.aspx"
 title="Creating Managed SQL Functions and Stored Procedures"
 description="See how to create SQL functions and stored procedures
 using managed code." />
</siteMapNode>

更新Web.sitemap文件后,花點(diǎn)時(shí)間在瀏覽器里查看,左邊的菜單將包括本部分的內(nèi)容.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖3:網(wǎng)站地圖現(xiàn)在包含了不部分的頁(yè)面

第二步:設(shè)置TableAdapter創(chuàng)建新的存儲(chǔ)過程

  我們?cè)趡/App_Code/DAL文件夾里創(chuàng)建一個(gè)類型化的DataSet,名稱為NorthwindWithSprocs.xsd.由于我們?cè)谝郧暗慕坛汤镆呀?jīng)詳細(xì)探討了創(chuàng)建細(xì)節(jié),因此我們這里一筆帶過,如果你想知道詳細(xì)的創(chuàng)建過程請(qǐng)參閱前面的第1章《創(chuàng)建一個(gè)數(shù)據(jù)訪問層》在DAL文件夾上右擊鼠標(biāo)選“添加新項(xiàng)”,選DataSet模板,如圖4所示.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖4:新建一個(gè)名為NorthwindWithSprocs.xsd的數(shù)據(jù)集

  這樣將會(huì)創(chuàng)建一個(gè)新的類型化的DataSet,打開設(shè)計(jì)器,創(chuàng)建一個(gè)新的TableAdapter,展開TableAdapter設(shè)置向?qū)?向?qū)У牡谝徊绞亲屛覀冞x擇要連接的數(shù)據(jù)庫(kù).在下拉列表里有一個(gè)連接到Northwind數(shù)據(jù)庫(kù)的連接字符串,選中它,再點(diǎn)下一步。接下來(lái)的界面讓我們選擇TableAdapter以哪種方式訪問數(shù)據(jù)庫(kù).在以前的教程里我們選擇的是“Use SQL statements”,不過在本文我們選第二項(xiàng):“Create new stored procedures”,點(diǎn)下一步.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖5:設(shè)置TableAdpater創(chuàng)建新的存儲(chǔ)過程

接下來(lái),我們要指定主查詢(main query).我們將創(chuàng)建一個(gè)存儲(chǔ)過程來(lái)包含SELECT查詢.
使用下面的SELECT查詢:

?
1
2
3
4
SELECT ProductID, ProductName, SupplierID, CategoryID,
 QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
 ReorderLevel, Discontinued
FROM Products

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖6:鍵入SELECT查詢

  注意:在名為Northwind的數(shù)據(jù)集里的ProductsTableAdapter的主查詢與上面本文定義的主查詢有所不同。那個(gè)主查詢還返回了每個(gè)產(chǎn)品的category名稱和company名稱.不過在后面的文章我們將對(duì)本文的TableAdapter添加這些相關(guān)的代碼.再點(diǎn)“Advanced Options”按鈕.我們可以指定是否讓向?qū)門ableAdapter自動(dòng)生成insert, update和delete statements;是否使用開發(fā)式并發(fā)操作(optimistic concurrency);是否完成inserts 和 update操作后刷新數(shù)據(jù)表.在默認(rèn)情況下,自動(dòng)選中“Generate Insert, Update and Delete statements”選項(xiàng)。另外,本文不用選擇“Use optimistic concurrency”項(xiàng).當(dāng)選擇自動(dòng)創(chuàng)建存儲(chǔ)過程時(shí),“Refresh the data table”項(xiàng)將被忽略掉.不管是否選中該項(xiàng),最終的insert 和update存儲(chǔ)過程都會(huì)檢索剛添加或剛更新(just-inserted or just-updated record)的記錄,我們將在第三步看到.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖7:選中“Generate Insert, Update and Delete statements”項(xiàng)

  注意:當(dāng)選中“Use optimistic concurrency”項(xiàng)的時(shí)候,向?qū)?huì)在WHERE語(yǔ)句里添加額外的條件,當(dāng)其它列的值發(fā)生改動(dòng)的話,將阻止數(shù)據(jù)更新.關(guān)于使用TableAdapter內(nèi)置的optimistic concurrency功能請(qǐng)參閱第21章《實(shí)現(xiàn)開放式并發(fā)》輸入SELECT主查詢并選取“Generate Insert, Update and Delete statements”項(xiàng)后,點(diǎn)下一步,接下來(lái)的界面,如圖8所示,讓我們?yōu)閟electing, inserting, updating, 和deleting數(shù)據(jù)的存儲(chǔ)過程命名.將這些存儲(chǔ)過程的名字改為Products_Select, Products_Insert, Products_Update, 和Products_Delete.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖8:為存儲(chǔ)過程重命名

向?qū)?chuàng)建了4個(gè)存儲(chǔ)過程,點(diǎn)“Preview SQL Script”按鈕,你可以在Preview SQL Script 對(duì)話框里將腳本保存在一個(gè)文件里或復(fù)制到剪貼板.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖9:預(yù)覽生成的存儲(chǔ)過程

  對(duì)存儲(chǔ)過程重命名后,點(diǎn)下一步,對(duì)TableAdapter相應(yīng)的方法命名.就像使用SQL statements一樣,我們可以創(chuàng)建方法來(lái)填充一個(gè)現(xiàn)有的DataTable或返回一個(gè)新的DataTable;我們也一個(gè)指定TableAdapter是否采用DB-Direct模式來(lái)插入、更新、刪除記錄.全選這3項(xiàng),只不過將Return a DataTable方法重命名為GetProducts,如圖10所示:

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖10:將方法重命名為Fill 和GetProducts

點(diǎn)Next總覽向?qū)?zhí)行的步驟.點(diǎn)Finish按鈕完成設(shè)置.一旦向?qū)ЫY(jié)束后,將返回DataSet設(shè)計(jì)器,它此時(shí)將包括ProductsDataTable.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖11:DataSet設(shè)計(jì)器將顯示剛剛添加的ProductsDataTable

第三步:考察剛剛創(chuàng)建的存儲(chǔ)過程

  我們?cè)诘诙嚼镉孟驅(qū)?chuàng)建了選擇、插入、更新、刪除數(shù)據(jù)的存儲(chǔ)過程.這些存儲(chǔ)過程可以通過Visual Studio查看或修改.打開服務(wù)器資源管理器,點(diǎn)到數(shù)據(jù)庫(kù)的存儲(chǔ)過程文件夾。如圖12所示,Northwind數(shù)據(jù)庫(kù)包含了4個(gè)新的存儲(chǔ)過程,Products_Delete, Products_Insert, Products_Select, and Products_Update.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖12:可以在Stored Procedures文件夾里找到我們創(chuàng)建的4個(gè)存儲(chǔ)過程

  注意:如果你看不到服務(wù)器資源管理器,點(diǎn)“View”菜單,選Server Explorer項(xiàng).如果你無(wú)法找到新創(chuàng)建的存儲(chǔ)過程,右擊Stored Procedures文件夾,選“刷新”.

  要查看或修改某個(gè)存儲(chǔ)過程,在服務(wù)器資源管理器里雙擊其名字或右擊該存儲(chǔ)過程,選”打開“。如13顯示的是打開Products_Delete存儲(chǔ)過程的畫面.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖13:可以在Visual Studio里打開并修改存儲(chǔ)過程

Products_Delete和Products_Select存儲(chǔ)過程的內(nèi)容很好理解。比如下面的代碼構(gòu)成了Products_Insert存儲(chǔ)過程.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ALTER PROCEDURE dbo.Products_Insert
(
 @ProductName nvarchar(40),
 @SupplierID int,
 @CategoryID int,
 @QuantityPerUnit nvarchar(20),
 @UnitPrice money,
 @UnitsInStock smallint,
 @UnitsOnOrder smallint,
 @ReorderLevel smallint,
 @Discontinued bit
)
AS
 SET NOCOUNT OFF;
INSERT INTO [Products] ([ProductName], [SupplierID], [CategoryID], [QuantityPerUnit],
 [UnitPrice], [UnitsInStock], [UnitsOnOrder], [ReorderLevel], [Discontinued])
VALUES (@ProductName, @SupplierID, @CategoryID, @QuantityPerUnit, @UnitPrice,
 @UnitsInStock, @UnitsOnOrder, @ReorderLevel, @Discontinued);
 
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice,
 UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued
FROM Products
WHERE (ProductID = SCOPE_IDENTITY())

  在TableAdapter向?qū)Ю锒x的SELECT查詢返回Products表里的列,這些列又作為存儲(chǔ)過程的輸入?yún)?shù)并運(yùn)用到INSERT statement中.緊接著的是一個(gè)SELECT查詢,返回Products表里最新添加的記錄的各列的值(包括ProductID)。當(dāng)使用Batch Update模式添加一個(gè)新記錄時(shí),刷新功能是很有用的。因?yàn)樗鼘⒆钚绿砑拥腜roductRow instances實(shí)例的ProductID屬性賦值為數(shù)據(jù)庫(kù)指派的自增值.

  下面的代碼說(shuō)明了該功能.代碼創(chuàng)建了基于NorthwindWithSprocs數(shù)據(jù)集的ProductsTableAdapter以及ProductsDataTable。要向數(shù)據(jù)庫(kù)添加一個(gè)新的產(chǎn)品,我們要?jiǎng)?chuàng)建一個(gè)ProductsRow instance實(shí)例,對(duì)其賦值,并調(diào)用TableAdapter的Update方法,再傳遞給ProductsDataTable.在內(nèi)部,TableAdapter的Update方法遍歷傳遞給DataTable的所有ProductsRow instance實(shí)例(在本例,只有一個(gè)。因?yàn)槲覀冎惶砑恿艘粋€(gè)產(chǎn)品),并執(zhí)行相應(yīng)的insert, update, 或delete命令。此時(shí),執(zhí)行Products_Insert存儲(chǔ)過程,其向Products表添加一條新記錄,并返回該記錄的詳細(xì)信息,然后更新ProductsRow instance實(shí)例的ProductID值。Update方法完成后,我們就可以通過ProductsRow的ProductID屬性訪問新添加記錄的ProductID值了.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Create the ProductsTableAdapter and ProductsDataTable
NorthwindWithSprocsTableAdapters.ProductsTableAdapter productsAPI =
 new NorthwindWithSprocsTableAdapters.ProductsTableAdapter();
NorthwindWithSprocs.ProductsDataTable products =
 new NorthwindWithSprocs.ProductsDataTable();
 
// Create a new ProductsRow instance and set its properties
NorthwindWithSprocs.ProductsRow product = products.NewProductsRow();
product.ProductName = "New Product";
product.CategoryID = 1; // Beverages
product.Discontinued = false;
 
// Add the ProductsRow instance to the DataTable
products.AddProductsRow(product);
 
// Update the DataTable using the Batch Update pattern
productsAPI.Update(products);
 
// At this point, we can determine the value of the newly-added record's ProductID
int newlyAddedProductIDValue = product.ProductID;

類似的,Products_Update存儲(chǔ)過程的UPDATE statement后面也包含一個(gè)SELECT statement,如下:

?
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
ALTER PROCEDURE dbo.Products_Update
(
 @ProductName nvarchar(40),
 @SupplierID int,
 @CategoryID int,
 @QuantityPerUnit nvarchar(20),
 @UnitPrice money,
 @UnitsInStock smallint,
 @UnitsOnOrder smallint,
 @ReorderLevel smallint,
 @Discontinued bit,
 @Original_ProductID int,
 @ProductID int
)
AS
 SET NOCOUNT OFF;
UPDATE [Products]
SET [ProductName] = @ProductName, [SupplierID] = @SupplierID,
 [CategoryID] = @CategoryID, [QuantityPerUnit] = @QuantityPerUnit,
 [UnitPrice] = @UnitPrice, [UnitsInStock] = @UnitsInStock,
 [UnitsOnOrder] = @UnitsOnOrder, [ReorderLevel] = @ReorderLevel,
 [Discontinued] = @Discontinued
WHERE (([ProductID] = @Original_ProductID));
 
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit,
 UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued
FROM Products
WHERE (ProductID = @ProductID)

  我們注意到該存儲(chǔ)過程有2個(gè)關(guān)于ProductID的參數(shù),即@Original_ProductID 和@ProductID,這樣以來(lái)我們就可以對(duì)主鍵值進(jìn)行改動(dòng)了.舉個(gè)例子:有一個(gè)employee(雇員)數(shù)據(jù)庫(kù),每條employee記錄都用雇員的社保號(hào)碼作為其主鍵值.要想更改某條記錄的社保號(hào)碼,必須提供新的號(hào)碼以及原始號(hào)碼.不過對(duì)Products表來(lái)說(shuō)用不著,因?yàn)榱蠵roductID是一個(gè)唯一標(biāo)識(shí)列(IDENTITY column),不應(yīng)對(duì)其更改.實(shí)際上,Products_Update存儲(chǔ)過程里的UPDATE statement并沒有包含ProductID列,因此,如果在UPDATE statement的WHERE字句里使用@Original_ProductID的話,顯得多此一舉,而應(yīng)該使用@ProductID參數(shù).當(dāng)更新某個(gè)存儲(chǔ)過程的參數(shù)時(shí),TableAdapter里所有那些調(diào)用該存儲(chǔ)過程方法都應(yīng)該進(jìn)行更新.

第四步:修改存儲(chǔ)過程的參數(shù)并更新TableAdapter

  由于@Original_ProductID參數(shù)是多余的,讓我們將其從Products_Update存儲(chǔ)過程里完全清除.打開Products_Update存儲(chǔ)過程,刪除@Original_ProductID參數(shù),在UPDATE statement的WHERE字句里將@Original_ProductID改為@ProductID. 完成上述修改后,該存儲(chǔ)過程里的T-SQL看起來(lái)應(yīng)該和下面的差不多:

?
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
ALTER PROCEDURE dbo.Products_Update
(
 @ProductName nvarchar(40),
 @SupplierID int,
 @CategoryID int,
 @QuantityPerUnit nvarchar(20),
 @UnitPrice money,
 @UnitsInStock smallint,
 @UnitsOnOrder smallint,
 @ReorderLevel smallint,
 @Discontinued bit,
 @ProductID int
)
AS
 SET NOCOUNT OFF;
UPDATE [Products] SET [ProductName] = @ProductName, [SupplierID] = @SupplierID,
 [CategoryID] = @CategoryID, [QuantityPerUnit] = @QuantityPerUnit,
 [UnitPrice] = @UnitPrice, [UnitsInStock] = @UnitsInStock,
 [UnitsOnOrder] = @UnitsOnOrder, [ReorderLevel] = @ReorderLevel,
 [Discontinued] = @Discontinued
WHERE (([ProductID] = @ProductID));
 
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit,
 UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued
FROM Products
WHERE (ProductID = @ProductID)

  按Ctrl+S或點(diǎn)工具欄里的“保存”圖標(biāo),保存更改.此時(shí),Products_Update存儲(chǔ)過程不會(huì)執(zhí)行@Original_ProductID參數(shù),但TableAdapter仍然會(huì)傳遞該參數(shù).要想查看TableAdapter傳遞給Products_Update存儲(chǔ)過程的參數(shù),你可以在設(shè)計(jì)器里選中TableAdapter,轉(zhuǎn)到屬性窗口,點(diǎn)更新命令的參數(shù)集(UpdateCommand'sParameters collection)里的橢圓型區(qū)域,這樣將轉(zhuǎn)到Parameters Collection Editor對(duì)話框,如圖14所示:

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖14:對(duì)話框里列出了傳遞給Products_Update存儲(chǔ)過程的參數(shù)

要?jiǎng)h除參數(shù),只需選中它,再點(diǎn)Remove按鈕.

  要刷新參數(shù)的話,你也可以在設(shè)計(jì)器里選中TableAdapter,點(diǎn)右鍵選“設(shè)置”,這將會(huì)開啟TableAdapter設(shè)置向?qū)В谐隽擞糜趕elect, insert, updat和delete的存儲(chǔ)過程,并列出了這些存儲(chǔ)過程的輸入?yún)?shù).如果你在Update下拉列表里選Products_Update的話,你可以看到該存儲(chǔ)過程包含的輸入?yún)?shù)里已經(jīng)沒有包含@Original_ProductID了(見圖15),點(diǎn)Finish將對(duì)TableAdapter使用的參數(shù)集自動(dòng)更新.

 在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖15:你可以通過使用TableAdapter的設(shè)置向?qū)?lái)刷新參數(shù)集

第五步:添加額外的TableAdapter方法

  我們?cè)诘诙秸f(shuō)過,當(dāng)創(chuàng)建一個(gè)新的TableAdapter時(shí),很容易自動(dòng)地生成相應(yīng)的存儲(chǔ)過程,同樣我們也可以向TableAdapter添加額外的方法.作為演示,讓我們向ProductsTableAdapter添加一個(gè)方法GetProductByProductID(productID),該方法將一個(gè)ProductID作為輸入?yún)?shù),并返回該產(chǎn)品的詳細(xì)信息.在ProductsTableAdapter上點(diǎn)擊右鍵,選擇“添加查詢”.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖16:向TableAdapter添加新查詢

  這將開啟TableAdapter查詢?cè)O(shè)置向?qū)АJ紫龋驅(qū)⒃儐栆院畏N方式訪問數(shù)據(jù)庫(kù),我們將創(chuàng)建一個(gè)新的存儲(chǔ)過程,因此選“Create a new stored procedure”,再點(diǎn)Next.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖17:選中“Create a new stored procedure”項(xiàng)

  接下來(lái),向?qū)г儐栁覀儓?zhí)行哪種查詢,是返回一系列行?一個(gè)標(biāo)量值?又或者執(zhí)行UPDATE, INSERT,或 DELETE statement.由于GetProductByProductID(productID)方法將返回一行,我們選擇“SELECT which returns row”項(xiàng),再點(diǎn)Next.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖18:選擇“SELECT which returns row” 項(xiàng)

  接下來(lái)的界面將展示TableAdapter的主查詢,其僅僅列出了存儲(chǔ)過程的名字(也就是dbo.Products_Select).將其刪除,替換為如下的SELECT statement,它返回某個(gè)具體產(chǎn)品的所有列.

?
1
2
3
4
5
SELECT ProductID, ProductName, SupplierID, CategoryID,
 QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
 ReorderLevel, Discontinued
FROM Products
WHERE ProductID = @ProductID

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖19:將存儲(chǔ)過程的名字替換為一個(gè)SELECT查詢.

  接下來(lái)要對(duì)創(chuàng)建的存儲(chǔ)過程命名,輸入Products_SelectByProductID,點(diǎn)Next.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖20:將新存儲(chǔ)過程命名為Products_SelectByProductID

  最后一步將要我們對(duì)自動(dòng)生成的名字重新命名,并指定是否使用Fill a DataTable模式、是否使用Return a DataTable模式,抑或這2種模式都采用.就本文而言,都選中這2項(xiàng)并將方法重命名為FillByProductID 和 GetProductByProductID.點(diǎn)Next,再點(diǎn)Finish完成設(shè)置向?qū)?

 在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程

圖21:將TableAdapter的方法重命名為FillByProductID 和 GetProductByProductID

  完成向?qū)Ш螅琓ableAdapter將包含一個(gè)新的可用方法——GetProductByProductID(productID),當(dāng)調(diào)用該方法時(shí),將執(zhí)行我們剛剛創(chuàng)建的Products_SelectByProductID存儲(chǔ)過程.花點(diǎn)時(shí)間在服務(wù)器資源管理器里查看該存儲(chǔ)過程,點(diǎn)Stored Procedures文件夾,并打開Products_SelectByProductID(如果你沒看到它,在Stored Procedures文件夾上右擊鼠標(biāo),選“刷新”).

  請(qǐng)注意,SelectByProductID存儲(chǔ)過程將@ProductID作為輸入?yún)?shù),并執(zhí)行我們?cè)谙驅(qū)Ю镙斎氲腟ELECT Statement,如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
ALTER PROCEDURE dbo.Products_SelectByProductID
(
 @ProductID int
)
AS
 SET NOCOUNT ON;
 
SELECT ProductID, ProductName, SupplierID, CategoryID,
 QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
 ReorderLevel, Discontinued
FROM Products
WHERE ProductID = @ProductID

第六步:創(chuàng)建一個(gè)業(yè)務(wù)邏輯層類

在我們打算從表現(xiàn)層訪問產(chǎn)品前,我們首先需要為新添加的數(shù)據(jù)集創(chuàng)建一個(gè)BLL class,在~/App_Code/BLL文件夾里創(chuàng)建一個(gè)ProductsBLLWithSprocs.cs文件,如下:

?
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
using System;
using System.Data;
using System.Configuration;
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 NorthwindWithSprocsTableAdapters;
 
[System.ComponentModel.DataObject]
public class ProductsBLLWithSprocs
{
 private ProductsTableAdapter _productsAdapter = null;
 protected ProductsTableAdapter Adapter
 {
 get
 {
  if (_productsAdapter == null)
  _productsAdapter = new ProductsTableAdapter();
 
  return _productsAdapter;
 }
 }
 
 [System.ComponentModel.DataObjectMethodAttribute
 (System.ComponentModel.DataObjectMethodType.Select, true)]
 public NorthwindWithSprocs.ProductsDataTable GetProducts()
 {
 return Adapter.GetProducts();
 }
 
 
 [System.ComponentModel.DataObjectMethodAttribute
 (System.ComponentModel.DataObjectMethodType.Select, false)]
 public NorthwindWithSprocs.ProductsDataTable GetProductByProductID(int productID)
 {
 return Adapter.GetProductByProductID(productID);
 }
 
 
 [System.ComponentModel.DataObjectMethodAttribute
 (System.ComponentModel.DataObjectMethodType.Insert, true)]
 public bool AddProduct
 (string productName, int? supplierID, int? categoryID,
  string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
  short? unitsOnOrder, short? reorderLevel, bool discontinued)
 {
 // Create a new ProductRow instance
 NorthwindWithSprocs.ProductsDataTable products =
  new NorthwindWithSprocs.ProductsDataTable();
 NorthwindWithSprocs.ProductsRow product = products.NewProductsRow();
 
 product.ProductName = productName;
 if (supplierID == null)
  product.SetSupplierIDNull();
 else
  product.SupplierID = supplierID.Value;
 if (categoryID == null)
  product.SetCategoryIDNull();
 else
  product.CategoryID = categoryID.Value;
 if (quantityPerUnit == null)
  product.SetQuantityPerUnitNull();
 else
  product.QuantityPerUnit = quantityPerUnit;
 if (unitPrice == null)
  product.SetUnitPriceNull();
 else
  product.UnitPrice = unitPrice.Value;
 if (unitsInStock == null)
  product.SetUnitsInStockNull();
 else
  product.UnitsInStock = unitsInStock.Value;
 if (unitsOnOrder == null)
  product.SetUnitsOnOrderNull();
 else
  product.UnitsOnOrder = unitsOnOrder.Value;
 if (reorderLevel == null)
  product.SetReorderLevelNull();
 else
  product.ReorderLevel = reorderLevel.Value;
 product.Discontinued = discontinued;
 
 // Add the new product
 products.AddProductsRow(product);
 int rowsAffected = Adapter.Update(products);
 
 // Return true if precisely one row was inserted, otherwise false
 return rowsAffected == 1;
 }
 
 [System.ComponentModel.DataObjectMethodAttribute
 (System.ComponentModel.DataObjectMethodType.Update, true)]
 public bool UpdateProduct
 (string productName, int? supplierID, int? categoryID, string quantityPerUnit,
 decimal? unitPrice, short? unitsInStock, short? unitsOnOrder,
 short? reorderLevel, bool discontinued, int productID)
 {
 NorthwindWithSprocs.ProductsDataTable products =
  Adapter.GetProductByProductID(productID);
 if (products.Count == 0)
  // no matching record found, return false
  return false;
 
 NorthwindWithSprocs.ProductsRow product = products[0];
 
 product.ProductName = productName;
 if (supplierID == null)
  product.SetSupplierIDNull();
 else
  product.SupplierID = supplierID.Value;
 if (categoryID == null)
  product.SetCategoryIDNull();
 else
  product.CategoryID = categoryID.Value;
 if (quantityPerUnit == null)
  product.SetQuantityPerUnitNull();
 else
  product.QuantityPerUnit = quantityPerUnit;
 if (unitPrice == null)
  product.SetUnitPriceNull();
 else
  product.UnitPrice = unitPrice.Value;
 if (unitsInStock == null)
  product.SetUnitsInStockNull();
 else
  product.UnitsInStock = unitsInStock.Value;
 if (unitsOnOrder == null)
  product.SetUnitsOnOrderNull();
 else
  product.UnitsOnOrder = unitsOnOrder.Value;
 if (reorderLevel == null)
  product.SetReorderLevelNull();
 else
  product.ReorderLevel = reorderLevel.Value;
 product.Discontinued = discontinued;
 
 // Update the product record
 int rowsAffected = Adapter.Update(product);
 
 // Return true if precisely one row was updated, otherwise false
 return rowsAffected == 1;
 }
 
 [System.ComponentModel.DataObjectMethodAttribute
 (System.ComponentModel.DataObjectMethodType.Delete, true)]
 public bool DeleteProduct(int productID)
 {
 int rowsAffected = Adapter.Delete(productID);
 
 // Return true if precisely one row was deleted, otherwise false
 return rowsAffected == 1;
 }
}

  該類和以前章節(jié)所創(chuàng)建的ProductsBLL class類差不多,只是它用的是數(shù)據(jù)集 NorthwindWithSprocs的ProductsTableAdapter 和 ProductsDataTable object對(duì)象。與ProductsBLL類使用using NorthwindTableAdapters不同,ProductsBLLWithSprocs類使用的是using NorthwindWithSprocsTableAdapters.同樣的,該類的ProductsDataTable和 ProductsRow對(duì)象使用的是NorthwindWithSprocs命名空間.我們的ProductsBLLWithSprocs class類提供了2種數(shù)據(jù)訪問方法GetProducts() 和GetProductByProductID().另外,還有添加、更新、刪除單個(gè)產(chǎn)品的方法.

第七步:在表現(xiàn)層出來(lái)數(shù)據(jù)集NorthwindWithSprocs

  此時(shí),我們以及對(duì)數(shù)據(jù)訪問層和業(yè)務(wù)邏輯層做了相關(guān)改動(dòng),接下來(lái)我們要?jiǎng)?chuàng)建一個(gè)ASP.NET頁(yè)面調(diào)用BLL的ProductsBLLWithSprocs class類以展示、更新、刪除記錄.

  打開AdvancedDAL文件夾里的NewSprocs.aspx頁(yè)面,從工具箱拖一個(gè)GridView控件到頁(yè)面,設(shè)置其ID為Products. 從GridView的智能標(biāo)簽將其綁定到一個(gè)名為ProductsDataSource的ObjectDataSource,設(shè)置其調(diào)用ProductsBLLWithSprocs類.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖22:設(shè)置ObjectDataSource調(diào)用ProductsBLLWithSprocs類

  SELECT標(biāo)簽的下拉列表里有2個(gè)方法,GetProducts()和GetProductByProductID().由于我們將在GridView里顯示所有的產(chǎn)品,所以我們選GetProducts()方法.在UPDATE, INSERT, 和DELETE標(biāo)簽里都只有一個(gè)方法,確保選中它們,點(diǎn)Finish按鈕。

  完成設(shè)置后,Visual Studio會(huì)向GridView添加BoundFields列以及一個(gè)CheckBoxField列, 啟用GridView控件的“編輯”和“刪除”功能.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖23:頁(yè)面包含一個(gè)可以分頁(yè)和排序的GridView控件.

  就像在以前的教程里探討過的一樣,完成ObjectDataSource的設(shè)置后,Visual Studio 會(huì)自動(dòng)的將OldValuesParameterFormatString屬性設(shè)置為“original_{0}”. 為使數(shù)據(jù)修改功能正常工作,要么將該屬性刪除,要么將其設(shè)置為“{0}”.

  在我們完成設(shè)置、啟用“編輯”和“刪除”功能、將OldValuesParameterFormatString屬性設(shè)為其默認(rèn)值后,頁(yè)面的聲明代碼看起來(lái)應(yīng)該和下面的差不多:

?
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False"
 DataKeyNames="ProductID" DataSourceID="ProductsDataSource">
 <Columns>
 <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
 <asp:BoundField DataField="ProductID" HeaderText="ProductID"
  InsertVisible="False" ReadOnly="True"
  SortExpression="ProductID" />
 <asp:BoundField DataField="ProductName" HeaderText="ProductName"
  SortExpression="ProductName" />
 <asp:BoundField DataField="SupplierID" HeaderText="SupplierID"
  SortExpression="SupplierID" />
 <asp:BoundField DataField="CategoryID" HeaderText="CategoryID"
  SortExpression="CategoryID" />
 <asp:BoundField DataField="QuantityPerUnit" HeaderText="QuantityPerUnit"
  SortExpression="QuantityPerUnit" />
 <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice"
  SortExpression="UnitPrice" />
 <asp:BoundField DataField="UnitsInStock" HeaderText="UnitsInStock"
  SortExpression="UnitsInStock" />
 <asp:BoundField DataField="UnitsOnOrder" HeaderText="UnitsOnOrder"
  SortExpression="UnitsOnOrder" />
 <asp:BoundField DataField="ReorderLevel" HeaderText="ReorderLevel"
  SortExpression="ReorderLevel" />
 <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
  SortExpression="Discontinued" />
 </Columns>
</asp:GridView>
 
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
 DeleteMethod="DeleteProduct" InsertMethod="AddProduct"
 SelectMethod="GetProducts" TypeName="ProductsBLLWithSprocs"
 UpdateMethod="UpdateProduct">
 <DeleteParameters>
 <asp:Parameter Name="productID" Type="Int32" />
 </DeleteParameters>
 <UpdateParameters>
 <asp:Parameter Name="productName" Type="String" />
 <asp:Parameter Name="supplierID" Type="Int32" />
 <asp:Parameter Name="categoryID" Type="Int32" />
 <asp:Parameter Name="quantityPerUnit" Type="String" />
 <asp:Parameter Name="unitPrice" Type="Decimal" />
 <asp:Parameter Name="unitsInStock" Type="Int16" />
 <asp:Parameter Name="unitsOnOrder" Type="Int16" />
 <asp:Parameter Name="reorderLevel" Type="Int16" />
 <asp:Parameter Name="discontinued" Type="Boolean" />
 <asp:Parameter Name="productID" Type="Int32" />
 </UpdateParameters>
 <InsertParameters>
 <asp:Parameter Name="productName" Type="String" />
 <asp:Parameter Name="supplierID" Type="Int32" />
 <asp:Parameter Name="categoryID" Type="Int32" />
 <asp:Parameter Name="quantityPerUnit" Type="String" />
 <asp:Parameter Name="unitPrice" Type="Decimal" />
 <asp:Parameter Name="unitsInStock" Type="Int16" />
 <asp:Parameter Name="unitsOnOrder" Type="Int16" />
 <asp:Parameter Name="reorderLevel" Type="Int16" />
 <asp:Parameter Name="discontinued" Type="Boolean" />
 </InsertParameters>
</asp:ObjectDataSource>

  此時(shí),我們可以對(duì)GridView控件做些修改,比如在編輯界面里使用確認(rèn)控件,在CategoryID 和 SupplierID列放置DropDownList控件,當(dāng)點(diǎn)擊Delete按鈕時(shí)彈出確認(rèn)框等.由于在以前的教程我們探討過這些主題,我不打算在此多花筆墨。

  不管你做沒做這些改進(jìn),讓我們?cè)跒g覽器里對(duì)頁(yè)面測(cè)試,如圖24所示.在GridView控件里每行都可以編輯和刪除.

在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過程
圖24:可以通過GridView對(duì)產(chǎn)品進(jìn)行查看、編輯、刪除

結(jié)語(yǔ):

  類型化數(shù)據(jù)集里的TableAdapters可以通過ad-hoc SQL statement或存儲(chǔ)過程訪問數(shù)據(jù)庫(kù)里的數(shù)據(jù).當(dāng)處理存儲(chǔ)過程時(shí),我們要么使用現(xiàn)有的存儲(chǔ)過程,要么使用TableAdapter向?qū)?chuàng)建一個(gè)基于SELECT查詢的新的存儲(chǔ)過程.在本文,我們考察了如何自動(dòng)的創(chuàng)建一個(gè)存儲(chǔ)過程.

  雖然自動(dòng)創(chuàng)建可以節(jié)省時(shí)間,但是在某些情況下,向?qū)ё詣?dòng)創(chuàng)建的存儲(chǔ)過程與我們的期望值還是有差距.比如自動(dòng)創(chuàng)建的Products_Update存儲(chǔ)過程,它包含@Original_ProductID 和 @ProductID這2個(gè)參數(shù),但@Original_ProductID參數(shù)對(duì)我們來(lái)說(shuō)是多余的.

在接下來(lái)的文章,我們將考察TableAdapter使用現(xiàn)有的存儲(chǔ)過程的情況.

  祝編程快樂!

作者簡(jiǎn)介

  本系列教程作者 Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創(chuàng)始人,自1998年以來(lái)一直應(yīng)用 微軟Web技術(shù)。希望對(duì)大家的學(xué)習(xí)ASP.NET有所幫助。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美人shou交在线播放 | 欧美兽皇video | jk制服喷水| 黄 在线播放| 桥本有菜作品在线 | 超级毛片| 免费国产一级观看完整版 | 日本乱中文字幕系列在线观看 | 精品无人区麻豆乱码无限制 | 俄罗斯引擎首页进入 | 国产精品日本一区二区不卡视频 | 99热在线免费观看 | 先锋资源av| 国产极品麻豆91在线 | 久久全国免费观看视频 | 亚洲热图 | 欧美一区二区三区大片 | 九九国产在线观看 | 国产99在线a视频 | 九九99在线视频 | 动漫jk美女被爆羞羞漫画 | 免费在线公开视频 | chinese壮直男gay老年人 | 99av涩导航| 日本xxxxxxxxx高清hd | 亚洲444777KKK在线观看 | 俄罗斯三级完整版在线观看 | tolove第一季动画在线看 | 亚洲热影院| 91天堂国产在线 在线播放 | 操骚0| 美女林柏欣21p人体之仓之梦 | 波多野结衣被绝伦强在线观看 | 日韩精选在线 | 亚洲 欧美 中文 日韩 另类 | gay男强壮军人chinese | 亚洲国产欧美久久香综合 | 欧美三级不卡视频 | 黄德维 | 视频污版 | 欧美专区亚洲 |