導(dǎo)言
當(dāng)使用 GridView 、 DetailsView 或 FormView 控件的內(nèi)建插入、編輯或刪除特征時(shí),在用戶添加一條新記錄或更新 / 刪除一條現(xiàn)在記錄的過(guò)程中發(fā)生了多個(gè)步驟。正如我們之前一節(jié)里所討論的,在 GridView 中編輯一行時(shí),保存( Update )和取消( Cancel )按鈕將取代編輯( Edit )按鈕,并且綁定列轉(zhuǎn)換成 TextBox 。在用戶更新了數(shù)據(jù)并點(diǎn)擊保存按鈕之后,下述步驟在回傳時(shí)執(zhí)行:
1. 該 GridView 控件根據(jù)當(dāng)前編輯行的唯一標(biāo)識(shí)字段(通過(guò) DataKeyNames 屬性)組裝它的 ObjectDataSource 的 UpdateParameters 參數(shù),連同用戶輸入的值
2. 該 GridView 控件調(diào)用它的 ObjectDataSource 的 Update() 方法,它轉(zhuǎn)而調(diào)用潛在對(duì)象的適當(dāng)?shù)姆椒ǎ? ProductsDAL.UpdateProduct ,我們之前一節(jié)里)
3. 現(xiàn)在,這些隱含的數(shù)據(jù),包含保存后的更改,被重新綁定到 GridView 控件
在這一連串的步驟里,觸發(fā)了許多事件,這讓我們可以創(chuàng)建事件處理程序從而在需要的地方增加自定義邏輯。例如,在第 1 步之前,觸發(fā) GridView 的事件。在這里,如果有什么 validation 錯(cuò)誤我們可以取消更新請(qǐng)求。當(dāng)調(diào)用 Update() 方法時(shí),觸發(fā) ObjectDataSource 的 Updating 事件,提供了增加或自定義 UpdateParameters 的值的機(jī)會(huì)。在 ObjectDataSource 的潛在對(duì)象的方法完全執(zhí)行后,觸發(fā) ObjectDataSource 的 Updated 事件。針對(duì) Updated 事件的事件處理程序可以檢查更新操作的相關(guān)詳細(xì)信息,例如影響了多少行數(shù)據(jù),或者是否引發(fā)了一個(gè)異常。最后,在第 2 步之后, GridView 的 RowUpdated 事件觸發(fā);針對(duì)此事件的事件處理程序可以檢查關(guān)于剛剛完成的更新操作的相關(guān)額外信息。
圖 1 描述了使用 GridView 更新時(shí)這一系列連續(xù)的事件和步驟。圖 1 里的這個(gè)事件模式不僅是在 GridView 的更新操作。從 GridView 、 DetailsView 或者 FormView 里插入、更新或者刪除數(shù)據(jù)時(shí),數(shù)據(jù) Web 服務(wù)器控件和 ObjectDataSource 都會(huì)發(fā)生這一連串的 pre-level 和 post-level 的事件。
圖
1:
當(dāng)在
GridView
里更新數(shù)據(jù)時(shí),觸發(fā)一連串的
Pre-
和
Post-
事件
在這一節(jié)里,我們將探討使用這些事件從而擴(kuò)展 ASP.NET 數(shù)據(jù) Web 服務(wù)器控件的內(nèi)建插入、更新和刪除功能。我們也會(huì)看看如何自定義編輯界面從而僅僅更新部分產(chǎn)品字段。
第一步 : 更新產(chǎn)品的 ProductName 和 UnitPrice 字段
在之前一節(jié)的編輯界面里,包含了產(chǎn)品的所有字段并且它們都不是只讀的。如果我們從 GridView 中剔除一列( QuantityPerUnit ),那么當(dāng)更新時(shí)數(shù)據(jù) Web 服務(wù)器控件就不會(huì)設(shè)置 ObjectDataSource 的 QuantityPerUnit UpdateParameters 的值。 ObjectDataSource 則傳入一個(gè) null 值到 UpdateProduct 這個(gè)業(yè)務(wù)邏輯層的方法,它將當(dāng)前編輯的數(shù)據(jù)庫(kù)記錄的 QuantityPerUnit 字段更改為值。同樣地,如果是一個(gè)必需的字段,例如 ProductName ,如果從編輯界面中剔除了,那么此更新將會(huì)失敗并拋出一個(gè) “ Column 'ProductName' does not allow nulls ” 異常。導(dǎo)致這一現(xiàn)象的原因是 ObjectDataSource 被配置為調(diào)用 ProductsBLL 類的 UpdateProduct 方法,它預(yù)期每一個(gè)產(chǎn)品字段都對(duì)應(yīng)一個(gè)輸入?yún)?shù)。因此, ObjectDataSource 的 UpdateParameters 集合包含了該方法的每一個(gè)輸入?yún)?shù)。
如果我們希望提供一個(gè)允許最終用戶僅僅可以更新部分字段的數(shù)據(jù) Web 服務(wù)器控件,那么我們需要在 ObjectDataSource 的 Updating 事件處理程序中編程設(shè)置缺失的 UpdateParameters 值,或者創(chuàng)建并調(diào)用一個(gè)預(yù)期部分字段的 BLL 方法。讓我們?cè)诮酉聛?lái)的步驟中探討。
特別地,讓我們創(chuàng)建一個(gè)在一個(gè)可編輯的 GridView 中僅顯示 ProductName 和 UnitPrice 字段的頁(yè)面。這個(gè) GridView 的編輯界面將僅僅允許用戶更新兩個(gè)顯示的字段, ProductName 和 UnitPrice 。因?yàn)檫@個(gè)編輯界面僅僅提供產(chǎn)品的部分字段,我們需要?jiǎng)?chuàng)建一個(gè) ObjectDataSource ,它使用現(xiàn)有的 BLL 的 UpdateProduct 方法并在 Updating 事件處理程序中編程設(shè)置產(chǎn)品的缺少的字段的值,或者我們需要?jiǎng)?chuàng)建一個(gè)新的 BLL 方法,它僅接受那些在 GridView 中已經(jīng)定義的部分字段。在這一節(jié)里,我們使用后者,創(chuàng)建一個(gè) UpdateProduct 方法的重載,它提取 3 個(gè)輸入?yún)?shù): productName 、 unitPrice 和 productID :
[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Update, false)]
public bool UpdateProduct(string productName, decimal? unitPrice, int productID)
{
Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
if (products.Count == 0)
// no matching record found, return false
return false;
Northwind.ProductsRow product = products[0];
product.ProductName = productName;
if (unitPrice == null) product.SetUnitPriceNull(); else product.UnitPrice = unitPrice.Value;
// Update the product record
int rowsAffected = Adapter.Update(product);
// Return true if precisely one row was updated, otherwise false
return rowsAffected == 1;
}
跟原有的 UpdateProduct 方法類似,這個(gè)重載方法首先檢查是否在數(shù)據(jù)庫(kù)中存在一個(gè)指定 ProductID 的產(chǎn)品。如果不存在,它返回 false ,指示更新產(chǎn)品信息的請(qǐng)求失敗。否則,它因而更新現(xiàn)存的產(chǎn)品記錄的 ProductName 和 UnitPrice 字段并通過(guò)調(diào)用 TableAdapter 的 Update() 方法提交此更新,傳入 ProductsRow 實(shí)例。
通過(guò)這些對(duì)我們的 ProductsBLL 類的額外處理,我們現(xiàn)在可以創(chuàng)建一個(gè)簡(jiǎn)單 GridView 界面。打開(kāi) EditInsertDelete 文件夾中的 DataModificationEvents.aspx ,添加一個(gè) GridView 控件到頁(yè)面。新建一個(gè) ObjectDataSource 并配置它使用 ProductsBLL 類,它的 Select() 方法映射到 GetProducts , Update() 方法映射到僅接受 productName 、 unitPrice 和 productID 輸入?yún)?shù)的 UpdateProduct 方法重載。圖 2 展示了映射 ObjectDataSource 的 Update() 方法到 ProductsBLL 類的新的 UpdateProduct 方法重載時(shí)的數(shù)據(jù)源配置向?qū)А?
圖
2:
映射
ObjectDataSource
的
Update()
方法到新的
UpdateProduct
重載
因?yàn)槲覀兊睦訉H僅需要編輯數(shù)據(jù)的能力,不需要插入或刪除記錄,那么花些時(shí)間明確地指明該 ObjectDataSource 的 Insert() 和 Delete() 方法不會(huì)映射到 ProductsBLL 類的任何方法 -- 通過(guò)到 INSERT 和 DELETE 的 tab 頁(yè)并從下拉列表中選擇(無(wú))。
圖
3:
在
INSERT
和
DELETE
的
Tab
頁(yè),從下拉列表中選擇(無(wú))
完成了此向?qū)Ш螅瑥? GridView 的職能標(biāo)記里勾選上啟用編輯。
完成了此數(shù)據(jù)源配置向?qū)Р⒔壎ǖ? GridView 后, Visual Staudio 就已經(jīng)添加好它們的聲明語(yǔ)法。到源視圖察看該 ObjectDataSource 的聲明標(biāo)記,它將如下所示:
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" OldValuesParameterFormatString="original_{0}" SelectMethod="GetProducts" TypeName="ProductsBLL" UpdateMethod="UpdateProduct">
<UpdateParameters>
<asp:Parameter Name="productName" Type="String" />
<asp:Parameter Name="unitPrice" Type="Decimal" />
<asp:Parameter Name="productID" Type="Int32" />
</UpdateParameters>
</asp:ObjectDataSource>
因?yàn)? ObjectDataSource 的 Insert() 和 Delete() 方法沒(méi)有映射,也就沒(méi)有 InsertParameters 或者 DeleteParameters 片段。此外,因?yàn)榉椒ㄓ成涞? UpdateProduct 方法重載,它僅接受 3 個(gè)輸入?yún)?shù), UpdateParameters 片段也就僅包含 3 個(gè) Parameter 實(shí)例。
注意到 ObjectDataSource 的屬性被設(shè)置為 original_{0} 。這個(gè)屬性是使用數(shù)據(jù)源配置向?qū)r(shí) Visual Studio 自動(dòng)設(shè)置的。因此,由于我們的 BLL 方法不需要傳入原始的 ProductID 值,從 ObjectDataSource 的聲明語(yǔ)法中刪除所有這些屬性設(shè)置。
注意 : If you 如果你只是簡(jiǎn)單地在設(shè)計(jì)視圖里從屬性窗口清除 OldValuesParameterFormatString 屬性的值,這個(gè)屬性會(huì)依舊存在于聲明語(yǔ)法里,但會(huì)被設(shè)置為一個(gè)空字符串。要么從聲明語(yǔ)法里把該屬性通通刪掉,要么從屬性窗口,設(shè)置它的值為默認(rèn)值: {0} 。
雖然 ObjectDataSource 僅僅包含對(duì) productName 、 unitPrice 和 productID 的 UpdateParameters , Visual Studio 還是為產(chǎn)品的每一個(gè)字段添加一個(gè)綁定列或 CheckBoxField 。
圖
4:
對(duì)應(yīng)產(chǎn)品的每一個(gè)字段,
GridView
都包含一個(gè)
BoundField
或
CheckBoxField
當(dāng)最終用戶編輯一個(gè)產(chǎn)品并點(diǎn)擊它的保存按鈕( Update ),該 GridView 遍歷那些可編輯的字段,然后把用戶輸入的值賦值到 ObjectDataSource 的 UpdateParameters 集合里對(duì)應(yīng)的參數(shù)。如果沒(méi)有對(duì)應(yīng)的參數(shù), GridView 則添加一個(gè)到參數(shù)集合里。因此,如果我們的 GridView 包含對(duì)應(yīng)產(chǎn)品的所有字段的綁定列或 CheckBox 列,那么 ObjectDataSource 則會(huì)調(diào)用能接受這些參數(shù)的方法重載,而不顧 ObjectDataSource 的聲明標(biāo)記指定只接受 3 個(gè)輸入?yún)?shù)的事實(shí)(見(jiàn)圖 5 )。類似地,如果存在某些由 GridView 非只讀列構(gòu)成的組合,而沒(méi)有一個(gè) UpdateProduct 重載能接受相應(yīng)的參數(shù),則會(huì)在試圖保存時(shí)引發(fā)一個(gè)異常。
圖
5: GridView
將添加參數(shù)到
ObjectDataSource
的
UpdateParameters
集合
為了確保 ObjectDataSource 調(diào)用僅接受 productName 、 unitPrice 和 productID 參數(shù) 的 UpdateProduct 重載,我們需要限定 GridView 僅包含 ProductName 和 UnitPrice 這兩個(gè)可編輯的列。這可以通過(guò)刪除其他綁定列和 CheckBox 列,或者把這些列的 ReadOnly 屬性設(shè)置為 true 實(shí)現(xiàn),又或者上述兩種方法結(jié)合使用。在本節(jié)里讓我們簡(jiǎn)單地刪除除了 ProductName 和 UnitPrice 綁定列以外的 GridView 的所有列,然后, GridView 的聲明標(biāo)記將如下所示:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" EnableViewState="False">
<Columns>
<asp:CommandField ShowEditButton="True" />
<asp:BoundField DataField="ProductName" HeaderText="ProductName" SortExpression="ProductName" />
<asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" SortExpression="UnitPrice" />
</Columns>
</asp:GridView>
即使重載預(yù)期 3 個(gè)輸入?yún)?shù),在我們的 GridView 里僅包含兩個(gè)綁定列。這是因?yàn)? productID 輸入?yún)?shù)是一個(gè)主鍵( primary key )的值,它通過(guò)當(dāng)前編輯行的 DataKeyNames 屬性傳入值。
我們的 GridView 控件,連同這個(gè) UpdateProduct 重載,允許一個(gè)用戶僅僅編輯產(chǎn)品的名稱和單價(jià)而不會(huì)丟失產(chǎn)品的其他字段。
圖
6:
僅允許編輯
ProductName
和
Unit
Price
的界面
完善 UnitPrice 格式化
雖然圖 6 所示的 GridView 實(shí)例能夠正常工作, UnitPrice 字段還完全未被格式化,導(dǎo)致顯示價(jià)格時(shí)缺少貨幣符號(hào),還有 4 個(gè)小數(shù)位。為了在非編輯狀態(tài)的行應(yīng)用貨幣格式,只需要簡(jiǎn)單地設(shè)置 UnitPrice 綁定列的 DataFormatString 屬性為 {0:c} ,還有它的 HtmlEncode 屬性為 false 。
圖
7:
設(shè)置
UnitPrice
綁定列的
DataFormatString
和
HtmlEncode
屬性
通過(guò)此項(xiàng)更改,處于非編輯狀態(tài)的行將價(jià)格格式化為貨幣;然而,當(dāng)前編輯的行,仍然顯示為沒(méi)有貨幣符號(hào)并保留四位小數(shù)。
圖
8:
現(xiàn)在,非編輯狀態(tài)的行格式化為貨幣值
通過(guò)設(shè)置此綁定列的 ApplyFormatInEditMode 屬性為 true (默認(rèn)為 false ),可以把 DataFormatString 屬性里指定的格式化指令應(yīng)用到編輯界面。
圖
9:
設(shè)置此綁定列的
ApplyFormatInEditMode
屬性為
true
通過(guò)這個(gè)更改,當(dāng)前編輯行里顯示的 UnitPrice 值也被格式化為貨幣。
圖
10:
現(xiàn)在,當(dāng)前編輯行的值也被格式化為貨幣
然而,如果把產(chǎn)品價(jià)格更新為文本框里帶貨幣符號(hào)的值 – 例如 $19.00 – 則會(huì)拋出一個(gè) FormatException 異常。當(dāng) GridView 嘗試把用戶提供的值賦值到 ObjectDataSource 的 UpdateParameters 集合,它無(wú)法把 UnitPrice 字符串“ $<chmetcnv w:st="on" unitname="”" sourcevalue="19" hasspace="False" negative="False" numbertype="1" tcsc="0">19.00<span><span>”</span></span></chmetcnv> 轉(zhuǎn)換成參數(shù)要求的 decimal 類型(見(jiàn)圖 11 )。為了補(bǔ)救這個(gè)問(wèn)題我們可以為 GridView 的 RowUpdating 事件添加一個(gè)事件處理程序并讓它把用戶輸入的 UnitPrice 格式化為貨幣格式的 decimal 。
這個(gè) GridView 的 RowUpdating 事件接受的第二個(gè)參數(shù)是一個(gè) GridViewUpdateEventArgs 類型的對(duì)象,它包含一個(gè) NewValues 字典,當(dāng)中的每一個(gè)屬性保存著用戶輸入的值,準(zhǔn)備賦值到 ObjectDataSource 的 UpdateParameters 集合。我們可以重寫(xiě)現(xiàn)有的 NewValues 集合中的 UnitPrice 值為一個(gè)貨幣金額,通過(guò)下面的事件處理程序中的代碼進(jìn)行解析:
protected void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
if (e.NewValues["UnitPrice"] != null)
e.NewValues["UnitPrice"] = decimal.Parse(e.NewValues["UnitPrice"].ToString(), System.Globalization.NumberStyles.Currency);
}
如果用戶提供了一個(gè)諸如“ $<chmetcnv w:st="on" unitname="”" sourcevalue="19" hasspace="False" negative="False" numbertype="1" tcsc="0">19.00<span><span>”</span></span></chmetcnv> 的 UnitPrice 值,這個(gè)值會(huì)被通過(guò) Decimal.Parse 計(jì)算并解析為貨幣金額的值重寫(xiě)。這將正確的解析貨幣值,無(wú)論是貨幣符號(hào)、逗號(hào)、小數(shù)點(diǎn)、等等,并使用 System.Globalization 命名空間的 NumberStyles 枚舉 。
圖 11 展示因用戶輸入的 UnitPrice 值帶有貨幣符號(hào)引起的問(wèn)題,還展示了如果利用 GridView 的 RowUpdating 事件處理程序從而正確地解析這樣的輸入。
圖
11:
現(xiàn)在,當(dāng)前編輯行的
UnitPrice
值被格式化為貨幣金額
第二步 : 阻止 NULL UnitPrices
雖然數(shù)據(jù)庫(kù)被配置為允許 Products 表里的 UnitPrice 字段為 NULL 值,但我們可能希望防止用戶訪問(wèn)這個(gè)特別的頁(yè)面并指定一個(gè)空的 UnitPrice 值。更確切地說(shuō),如果用戶在編輯一個(gè)產(chǎn)品行記錄時(shí)忘記輸入一個(gè) UnitPrice 值, 與其保存這個(gè)結(jié)果到數(shù)據(jù)庫(kù),我們還不如給用戶顯示一個(gè)提示信息,自始至終這個(gè)頁(yè)面,任何的對(duì)產(chǎn)品的編輯必須指定一個(gè)價(jià)格。
傳入到 GridView 的事件處理程序的 GridViewUpdateEventArgs 對(duì)象包含一個(gè) Cancel 屬性,如果把它設(shè)置為 true ,則中止這個(gè)更新過(guò)程。讓我們擴(kuò)展 RowUpdating 事件處理程序,設(shè)置 e.Cancel 為 true 并顯示一個(gè)信息說(shuō)明為什么在 NewValues 集合里的 UnitPrice 值為 null 。
首先,添加一個(gè) Label 服務(wù)器控件到頁(yè)面并命名為 MustProvideUnitPriceMessage 。這個(gè) Label 控件將顯示用戶是否在更新一個(gè)產(chǎn)品時(shí)忘記指定一個(gè) UnitPrice 值。設(shè)置這個(gè) Label 的 Text 屬性為“您必須為產(chǎn)品提供一個(gè)價(jià)格。”。我也已經(jīng)在 Styles.css 文件中添加了一個(gè)名為 Warning 的新 CSS 類別,定義如下:
.Warning
{
color: Red;
font-style: italic;
font-weight: bold;
font-size: x-large;
}
最后,設(shè)置 Label 的 CssClass 屬性為 Warning 。這樣設(shè)計(jì)器中將在 GridView 上方顯示這個(gè)紅色、斜體、加粗、并且較大字體的警告信息。
圖
12:
一個(gè)
Label
控件已被添加到
GridView
上方
默認(rèn)地,這個(gè) Label 控件隱藏,那么在 Page_Load 事件處理程序中設(shè)置它的 Visible 屬性為 false 。
protected void Page_Load(object sender, EventArgs e)
{
MustProvideUnitPriceMessage.Visible = false;
}
如果用戶嘗試更新一個(gè)產(chǎn)品并不指定 UnitPrice 值,我們希望取消這個(gè)更新并顯示警告標(biāo)簽。在 GridView 的 RowUpdating 事件處理程序中增加如下的代碼:
protected void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
if (e.NewValues["UnitPrice"] != null)
e.NewValues["UnitPrice"] = decimal.Parse(e.NewValues["UnitPrice"].ToString(), System.Globalization.NumberStyles.Currency);
else
{
// Show the Label
MustProvideUnitPriceMessage.Visible = true;
// Cancel the update
e.Cancel = true;
}
}
如果一個(gè)用戶試圖保存一個(gè)產(chǎn)品并不指定價(jià)格,這個(gè)更新操作就被取消并顯示一個(gè)有用的提示信息。雖然數(shù)據(jù)庫(kù)(和業(yè)務(wù)邏輯)允許 NULL 值的 UnitPrice ,但這個(gè)特定的 ASP.NET 頁(yè)面不允許。
圖
13:
用戶不能讓
UnitPrice
為空
到目前為止我們看過(guò)了如何使用 GridView 的 RowUpdating 事件來(lái)編程改變賦值到 ObjectDataSource 的 UpdateParameters 集合的參數(shù)值,還有如何完全地取消這個(gè)更新過(guò)程。這些思想也可以延展至 DetailsView 和 FormView 控件并且應(yīng)用到插入或刪除。
這些任務(wù)也可以在 ObjectDataSource 這一層通過(guò)的 Inserting 、 Updating 和 Deleting 事件處理程序完成。這些事件在隱含對(duì)象的關(guān)聯(lián)方法被調(diào)用前觸發(fā),并且提供一個(gè)更改輸入?yún)?shù)集合或者完全取消此操作的最后機(jī)會(huì)。對(duì)應(yīng)這 3 個(gè)事件的事件處理程序傳入一個(gè) ObjectDataSourceMethodEventArgs 類型的對(duì)象,我們對(duì)它的這兩個(gè)屬性感興趣:
· Cancel ,它如果被設(shè)置為 true ,取消執(zhí)行中的操作
· InputParameters ,它是 InsertParameters 、 UpdateParameters 或 DeleteParameters 的集合,這取決于這是 Inserting 、 Updating 、 還是 Deleting 事件的事件處理程序
為了舉例說(shuō)明在 ObjectDataSource 這一層如何處理參數(shù)的值,讓我們?cè)陧?yè)面里包含一個(gè) DeailsView ,它允許用戶新增一個(gè)新的產(chǎn)品記錄。這個(gè) DetailsView 將用作提供一個(gè)快捷的方式添加一個(gè)新的產(chǎn)品記錄到數(shù)據(jù)庫(kù)。為了在新增產(chǎn)品時(shí)保持界面一致,我們僅允許用戶輸入 ProductName 和 UnitPrice 字段。作為默認(rèn)值,這些在 DetailsView 的插入界面提供的值將被設(shè)置為一個(gè)數(shù)據(jù)庫(kù)的 NULL 值。不過(guò),我們可以使用 ObjectDataSource 的 Inserting 事件注入不同的默認(rèn)值,正如我們馬上要看的。
第三步 : 提供一個(gè)添加新產(chǎn)品的界面
在 GridView 的上方,從工具箱里拖放一個(gè) DetailsView 控件到設(shè)計(jì)器,清空它的 Height 和 Width 屬性,并將它綁定到頁(yè)面中已經(jīng)存在的 ObjectDataSource 。這將為產(chǎn)品的每一個(gè)字段添加一個(gè)綁定列或 CheckBox 列。因?yàn)槲覀兿M眠@個(gè) DetailsView 控件來(lái)添加新產(chǎn)品,我們需要從它的職能標(biāo)記里勾選啟用插入這一項(xiàng);然而并沒(méi)有這一項(xiàng),這是因?yàn)榇? ObjectDataSource 的 Insert() 方法還沒(méi)有映射到 ProductsBLL 類的方法(回想起我們?cè)谂渲脭?shù)據(jù)源時(shí)設(shè)置了這個(gè)映射為(無(wú)) – 見(jiàn)圖 3 )。
為了再次配置這個(gè) ObjectDataSource ,從它的職能標(biāo)記中選擇“配置數(shù)據(jù)源”的鏈接,載入向?qū)А5谝黄猎试S你更改 ObjectDataSource 綁定到的隱含對(duì)象;讓它依舊是 ProductsBLL 。下一屏列出從 ObjectDataSource 到隱含對(duì)象的方法的映射。盡管我們已經(jīng)明確地指定 Insert() 和 Delete() 不映射到任何方法,然而如果你到 INSERT 和 DELETE 的 tab 頁(yè)你還是會(huì)看到那里有一個(gè)映射。這是因?yàn)? ProductsBLL 的 AddProduct 和 DeleteProduct 方法使用了 DataObjectMethodAttribute 指出它們分別是為 Insert() 和 Delete() 服務(wù)的默認(rèn)的方法。因此, ObjectDataSource 向?qū)г诿看芜\(yùn)行時(shí)都會(huì)自動(dòng)地選擇它們,除非那里被明確地指定了一個(gè)別的值。
讓 Insert() 方法依舊指向 AddProduct 方法,不過(guò)再次從 DELETE 的 tab 頁(yè)的下拉列表中選擇“ ( 無(wú) ) ”。
圖
14:
從
INSERT
的
Tab
頁(yè)的下拉列表中選擇
AddProduct
方法
圖
15:
從
DELETE
的
Tab
頁(yè)的下拉列表中選擇
(
無(wú)
)
完成了這些更改后,該 ObjectDataSource 的聲明語(yǔ)法中將包含一個(gè) InsertParameters 集合,如下所示:
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="GetProducts"
TypeName="ProductsBLL" UpdateMethod="UpdateProduct" OnUpdating="ObjectDataSource1_Updating" InsertMethod="AddProduct" OldValuesParameterFormatString="original_{0}">
<UpdateParameters>
<asp:Parameter Name="productName" Type="String" />
<asp:Parameter Name="unitPrice" Type="Decimal" />
<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>
再次運(yùn)行向?qū)?dǎo)致重新添加 OldValuesParameterFormatString 屬性。把這個(gè)屬性設(shè)置為默認(rèn)值( {0} )或者從聲明語(yǔ)法中把它們完全刪除。
隨著 ObjectDataSource 提供插入數(shù)據(jù)的能力,現(xiàn)在 DetailsView 的職能標(biāo)記里包含“啟用插入”的 checkbox ;回到設(shè)計(jì)器并勾選這一項(xiàng)。然后,減少 DetailsView 的列直道它只包含兩個(gè)綁定列 - ProductName 和 UnitPrice ,還有一個(gè) CommandField 。此時(shí) DetailsView 的聲明語(yǔ)法將如下所示:
<asp:DetailsView ID="DetailsView1" runat="server" AutoGenerateRows="False" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" EnableViewState="False">
<Fields>
<asp:BoundField DataField="ProductName" HeaderText="ProductName" SortExpression="ProductName" />
<asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" SortExpression="UnitPrice" />
<asp:CommandField ShowInsertButton="True" />
</Fields>
</asp:DetailsView>
圖 16 展現(xiàn)的是在此時(shí)通過(guò)瀏覽器查看的頁(yè)面。正如你所看到的, DetailsView 列出第一個(gè)產(chǎn)品( Chai )的名稱和價(jià)格。不過(guò),我們需要的是一個(gè)插入界面來(lái)提供一個(gè)用戶快速增加一個(gè)新產(chǎn)品到數(shù)據(jù)庫(kù)的手段。
圖
16:
該
DetailsView
當(dāng)前呈現(xiàn)在只讀模式
為了展示插入模式的 DetailsView 我們需要設(shè)置 DefaultMode 屬性為 Inserting 。這讓 DetailsView 在第一次訪問(wèn)和插入一條新紀(jì)錄之后,它都呈現(xiàn)在插入模式。如圖 17 所示,這樣的一個(gè) DetailsView 提供了一個(gè)快捷的界面用以添加新記錄。
圖
17:
這個(gè)
DetailsView
提供了一個(gè)快速添加新產(chǎn)品的界面
當(dāng)用戶輸入一個(gè)產(chǎn)品名稱和價(jià)格(例如“ Acme Wate ”和 1.99 ,如圖 17 )并點(diǎn)擊插入按鈕,發(fā)生回傳并且開(kāi)始插入的工作流程,直到最后添加一個(gè)新產(chǎn)品記錄到數(shù)據(jù)庫(kù)。 DetailsView 維持在它的插入界面,并且 GridView 自動(dòng)地重現(xiàn)綁定到它的數(shù)據(jù)源,目的是為了包含新增加的產(chǎn)品,如圖 18 所示。
圖
18:
產(chǎn)品“
Acme Water
”已經(jīng)被添加到數(shù)據(jù)庫(kù)
雖然圖 18 的 GridView 中沒(méi)有顯示出來(lái), DetailsView 界面里缺少的產(chǎn)品字段 – CategoryID 、 SupplierID 、 QuantityPerUnit 、等等 – 被賦上一個(gè)數(shù)據(jù)庫(kù) NULL 值。你可以通過(guò)履行下面一個(gè)步驟來(lái)看到這一點(diǎn):
1. 到 Visual Studio 的服務(wù)器資源管理器
2. 展開(kāi) NORTHWND.MDF 數(shù)據(jù)庫(kù)節(jié)點(diǎn)
3. 在 Products 數(shù)據(jù)表節(jié)電上右鍵點(diǎn)擊
4. 選擇“顯示表數(shù)據(jù)”
這將列出 Products 表中的所有記錄。如圖 19 所示,除了 ProductID 、 ProductName 和 UnitPrice 字段,我們的新產(chǎn)品的其它字段都為 NULL 值。
圖
19:
未在
DetailsView
中提供的產(chǎn)品的其它字段被賦予
NULL
值
我們可能希望給予一個(gè)或幾個(gè)這些字段的值一個(gè)默認(rèn)值,而不是 NULL ,要么是因?yàn)? NULL 并非最佳的默認(rèn)項(xiàng),要么是因?yàn)閿?shù)據(jù)庫(kù)字段本身不允許 NULL 值。為了實(shí)現(xiàn)這一點(diǎn),我們可以編程設(shè)置這些 DetailsView 的 InputParameters 集合里的參數(shù)值。這項(xiàng)工作可以在 DetailsView 的 ItemInserting 事件處理程序中完成,或者在 ObjectDataSource 的 Inserting 事件處理程序。因?yàn)槲覀円呀?jīng)看過(guò)了如何在數(shù)據(jù) Web 服務(wù)器控件中如何使用 pre- 和 post-level 的事件,這次讓我們探究一下 ObjectDataSource 的事件。
第四步 : 給 CategoryID 和 SupplierID 參數(shù)賦值
在這里我們假設(shè)當(dāng)我們的應(yīng)用程序通過(guò)這個(gè)界面添加一個(gè)新產(chǎn)品時(shí)應(yīng)該給 CategoryID 和 SupplierID 字段賦值為 1 。如之前提及的, ObjectDataSource 控件有一對(duì) pre- 和 post-level 的事件發(fā)生在數(shù)據(jù)更改過(guò)程中。當(dāng)它的 Insert() 方法被調(diào)用時(shí), ObjectDataSource 首先觸發(fā)它的 Inserting 事件,然后調(diào)用它的 Insert() 方法所映射到的業(yè)務(wù)方法,最后觸發(fā) Inserted 事件。這個(gè) Inserting 事件處理程序提供給我們一個(gè)處理輸入?yún)?shù)或者徹底取消此操作的最后機(jī)會(huì)。
注意 : 在一個(gè)真實(shí)的應(yīng)用程序中你很可能希望既讓用戶指定 category 和 supplier 的值,又希望基于一定的標(biāo)準(zhǔn)和業(yè)務(wù)邏輯的基礎(chǔ)上選擇這個(gè)值(而不是盲目地選擇 ID 為 1 )。不管如何,這個(gè)例子闡明了如何在 ObjectDataSource 的 pre-level 的事件中編程設(shè)置一個(gè)輸入?yún)?shù)的值。
花些時(shí)間為 ObjectDataSource 的 Inserting 事件創(chuàng)建一個(gè)事件處理程序。注意到該事件處理程序的第二個(gè)輸入?yún)?shù)是一個(gè) ObjectDataSourceMethodEventArgs 類型的對(duì)象,它有一個(gè)屬性來(lái)存取參數(shù)集合( InputParameters ),還有一個(gè)屬性用來(lái)取消此操作( Cancel )。
protected void ObjectDataSource1_Inserting(object sender, ObjectDataSourceMethodEventArgs e)
{
}
此時(shí), InputParameters 屬性包含了通過(guò) DetailsView 賦值到 ObjectDataSource 的 InsertParameters 集合。為了修改這些參數(shù)里的一個(gè)值,簡(jiǎn)單地使用: e.InputParameters[" paramName "] = value 。所以,為了設(shè)置 CategoryID 和 SupplierID 為 1 ,把 Inserting 事件處理程序調(diào)整為如下:
protected void ObjectDataSource1_Inserting(object sender, ObjectDataSourceMethodEventArgs e)
{
e.InputParameters["CategoryID"] = 1;
e.InputParameters["SupplierID"] = 1;
}
這次當(dāng)我們添加一個(gè)新產(chǎn)品(例如 Acme Soda ),這個(gè)新產(chǎn)品的 CategoryID 和 SupplierID 字段被賦值為 1 (見(jiàn)圖 20 )。
圖
20:
現(xiàn)在,新產(chǎn)品的
CategoryID
和
SupplierID
字段被設(shè)置為
1
總結(jié)
在編輯、插入和刪除的過(guò)程中,無(wú)論數(shù)據(jù) Web 服務(wù)器控件還是 ObjectDataSource 都會(huì)發(fā)生許多 pre- 和 post-level 的事件。在這一節(jié)里,我們研究了 pre-level 的事件,并看到了如何在數(shù)據(jù) Web 服務(wù)器控件和 ObjectDataSource 的事件里使用它們來(lái)自定義輸入?yún)?shù)或取消當(dāng)前的數(shù)據(jù)更改操作。下一節(jié),我們將看看創(chuàng)建和使用 post-level 的事件的事件處理程序。
祝編程快樂(lè)!
作者簡(jiǎn)介
Scott Mitchell ,著有六本 ASP/ASP.NET 方面的書(shū),是 4GuysFromRolla.com 的創(chuàng)始人,自 1998 年以來(lái)一直應(yīng)用微軟 Web 技術(shù)。 Scott 是個(gè)獨(dú)立的技 術(shù)咨詢顧問(wèn),培訓(xùn)師,作家,最近完成了將由 Sams 出版社出版的新作, 24 小時(shí)內(nèi)精通 ASP.NET 2.0 。他的聯(lián)系電郵為 mitchell@4guysfromrolla.com ,也可以通過(guò)他的博客 http://ScottOnWriting.NET 與他聯(lián)系。
Scott Mitchell 的ASP.NET 2.0數(shù)據(jù)教程之十七:: 研究插入、更新和刪除的關(guān)聯(lián)事件
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
