【ASP.NET Step by Step】之十六至二十三 Inserting, Updating, and Deleting Data
點(diǎn)擊GridView的刪除,事件觸發(fā)過(guò)程
GridView控件提供了對(duì)行編輯和刪除的內(nèi)建的支持。配置一個(gè)GridView支持刪除需要添加一個(gè)刪除按鈕列。當(dāng)最終用戶(hù)點(diǎn)擊某一特定行的刪除按鈕時(shí),引發(fā)一次回傳并且GridView執(zhí)行以下步驟:?
1.????? 對(duì)ObjectDataSource的DeleteParameters賦值
2.????? 調(diào)用ObjectDataSource的Delete()方法,刪除指定的記錄
3.????? 通過(guò)調(diào)用它的Select()方法GridView重新綁定到ObjectDataSource??
賦值到DeleteParameters的值是點(diǎn)擊刪除按鈕這一行的DataKeyNames字段的值。因此正確地設(shè)置GridView的DataKeyNames屬性是至關(guān)重要的。如果缺少了這個(gè),DeleteParameters將在第1步被賦上一個(gè)null值,從而在第2步中將不會(huì)導(dǎo)致刪除任何記錄。
點(diǎn)擊GridView的編輯,事件觸發(fā)過(guò)程
當(dāng)最終用戶(hù)點(diǎn)擊特定一行的編輯按鈕時(shí),引發(fā)一次回傳并且GridView執(zhí)行以下步驟:
??
?????2.?通過(guò)調(diào)用它的Select()方法,GridView重新綁定自己到ObjectDataSource
?????3.?與EditItemIndex相匹配的行呈現(xiàn)為編輯模式。在此模式下,Edit按鈕替換為Update和Cancel按鈕,并且那些ReadOnly屬性為False的綁定列呈現(xiàn)為
?????????TextBox服務(wù)器控件,這些TextBox的Text屬性被賦值為相應(yīng)的數(shù)據(jù)字段的值。
?
到這里HTML標(biāo)記被返回到瀏覽器,允許最終用戶(hù)可以修改行數(shù)據(jù)。當(dāng)用戶(hù)點(diǎn)擊保存按鈕,再次發(fā)生一次回傳,并且GridView執(zhí)行以下幾個(gè)步驟:
?????1.?ObjectDataSource的UpdateParameters的值被賦值為最終用戶(hù)在GridView的編輯界面輸入的值
?????2.?調(diào)用ObjectDataSource的Update()方法,更新指定的記錄
?????3.?通過(guò)調(diào)用Select()方法,GridView重新綁定自己到ObjectDataSource
GridView的DataKeyNames屬性指定的主鍵的值在第1步中賦值到UpdateParameters,反之非主鍵的值來(lái)自當(dāng)前編輯行的TextBox服務(wù)器控件。如果DataKeyNames遺漏了,那么UpdateParameters主鍵的值在第1步中將被賦上一個(gè)空值,然后轉(zhuǎn)入第2步中將不會(huì)導(dǎo)致任何記錄的更新。
也可以這樣說(shuō):基于GridView的DataKeyNames里的主鍵值和GridView里的值,組成ObjectDataSOurce的UpdateParameters
如圖所示,更新數(shù)據(jù),觸發(fā)一連串的Pre-和Post-事件
注意:
我們創(chuàng)建的BLL層UpdateProduct方法是接受所有參數(shù)的,所以如果我們的GridView里綁定的列少了一些,
編輯的時(shí)候,這些沒(méi)綁定的非只讀列會(huì)被ObjectDataSource自做主張的置為NULL傳給BLL層;
因此如果GridView少了ProductName,因?yàn)樗且蠓强盏?#xff0c;所以Update的時(shí)候就產(chǎn)生了異常。
所以,我們綁定幾列,就應(yīng)該在BLL層重載UpdateProduct(幾列)方法。
例如,我們?cè)贐LL重載了帶三個(gè)參數(shù)的UpdateProduct方法,并且指定ObjectDataSource控件的Update方法為三個(gè)參數(shù)的UpdatProduct
????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>
這時(shí),如果GridView是所有列,那么ObjectDataSource則會(huì)調(diào)用能接受這些參數(shù)的方法重載,
而不顧ObjectDataSource的聲明標(biāo)記指定只接受3個(gè)輸入?yún)?shù)的事實(shí)。
而如果GridView含有4個(gè)列,則會(huì)在試圖保存時(shí)引發(fā)一個(gè)異常
could not find a non-generic method 'UpdateProduct' that has parameters: ProductName, UnitPrice, ProductID, QuantityPerUnit.
注意Added by GridView
Pre級(jí)事件處理UnitPrice格式問(wèn)題
UnitPrice的顯示格式是{0:C}, 如果想讓編輯時(shí)候也顯示這個(gè)狀態(tài),要設(shè)置綁定列的ApplyFormatInEditMode屬性為true,
這樣設(shè)置后產(chǎn)生的新問(wèn)題是當(dāng)GridView嘗試把用戶(hù)提供的值賦值到ObjectDataSource的UpdateParameters集合,它無(wú)法把UnitPrice字符串“$19.00”轉(zhuǎn)換成參數(shù)要求的decimal類(lèi)型,
解決方法:
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è)NewValues字典,當(dāng)中的每一個(gè)屬性保存著用戶(hù)輸入的值,
e.Cancel=true;? 說(shuō)明當(dāng)用戶(hù)忘記輸入U(xiǎn)nitPrice,則顯示提示信息,并Cancel掉更新。
Bind()?和 Eval()
Code
1?<EditItemTemplate>
2?????????????ProductID:
3?????????????<asp:Label?ID="ProductIDLabel1"?runat="server"?
4?????????????????Text='<%#?Eval("ProductID")?%>'?/>
5?????????????<br?/>
6?????????????ProductName:
7?????????????<asp:TextBox?ID="ProductNameTextBox"?runat="server"?
8?????????????????Text='<%#?Bind("ProductName")?%>'?/>
9?????????????<br?/>
<%# Bind("dataField") %>?? 雙向綁定,
用戶(hù)點(diǎn)擊編輯按鈕,Bind()綁定數(shù)據(jù)到模版(TextBox),
點(diǎn)擊Update按鈕,通過(guò)Bind()指定的數(shù)據(jù)字段的值被回傳到ObjectDataSource的UpdateParameters
<%# Eval("dataField") %>?? 單向綁定,
僅僅在綁定數(shù)據(jù)到模版時(shí)取得數(shù)據(jù)字段的值,但并不會(huì)在回傳時(shí)將用戶(hù)輸入的值返回到數(shù)據(jù)源控件的參數(shù)
Post級(jí)事件用于異常處理
1. 如果數(shù)據(jù)庫(kù)不正常運(yùn)作,則在試圖連接數(shù)據(jù)庫(kù)時(shí)通過(guò)TableAdapter拋出一個(gè)SqlException異常。
2. DAL層異常,遺漏了ProductName值則引發(fā)拋出一個(gè)NoNullAllowedException異常,因?yàn)镻rodcutsRow類(lèi)的ProductName屬性設(shè)置了它的AllowDBNull屬性為false(這句話(huà)好像有點(diǎn)問(wèn)題)
3. BLL層異常,
數(shù)據(jù)庫(kù)這樣的設(shè)置 .NET在創(chuàng)建DataSet時(shí)做了什么呢?
.NET為我們做了:
?????private?ProductsDataTable?tableProducts;
?????[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
??????public?string?ProductName?{
????????????get?{
????????????????return?((string)(this[this.tableProducts.ProductNameColumn]));
????????????}
????????????set?{
????????????????this[this.tableProducts.ProductNameColumn]?=?value;
????????????}
??????}
????//
}
public?partial?class?ProductsDataTable?:?global::System.Data.TypedTableBase<ProductsRow>?{
????private?global::System.Data.DataColumn?columnProductName;
???[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
????????public?ProductsDataTable()?{
????????????this.TableName?=?"Products";
????????????this.BeginInit();
????????????this.InitClass();
????????????this.EndInit();
????????}
???//
???[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
???public?global::System.Data.DataColumn?ProductNameColumn?{
????????????get?{
????????????????return?this.columnProductName;
????????????}
???}
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
private?void?InitClass()?{
this.columnProductName.AllowDBNull?=?false;
this.columnProductName.MaxLength?=?40;
//
}
夠復(fù)雜吧
還是回到主題,沒(méi)有任何的動(dòng)作,這些異常都會(huì)從數(shù)據(jù)訪問(wèn)層冒出到業(yè)務(wù)邏輯層,然后到ASP.NET頁(yè)面,最后到ASP.NET運(yùn)行時(shí)
用戶(hù)是不希望看到這樣的東西的。人性化的方法是我們提前處理這些異常,給用戶(hù)友好的界面。
ObjectDataSource 和數(shù)據(jù)Web控件的post級(jí)事件都提供了發(fā)現(xiàn)并不讓它出現(xiàn)在ASP.NET運(yùn)行時(shí)的方法
下面的代碼實(shí)現(xiàn)了上述的目的:
?2{
?3????if?(e.Exception?!=?null)
?4????{
?5????????//?Display?a?user-friendly?message
?6????????ExceptionDetails.Visible?=?true;
?7????????ExceptionDetails.Text?=?"There?was?a?problem?updating?the?product.?";
?8
?9????????if?(e.Exception.InnerException?!=?null)
10????????{
11????????????Exception?inner?=?e.Exception.InnerException;
12
13????????????if?(inner?is?System.Data.Common.DbException)
14????????????????ExceptionDetails.Text?+=?"Our?database?is?currently?experiencing?problems.?Please?try?again?later.";
15????????????else?if?(inner?is?NoNullAllowedException)
16????????????????ExceptionDetails.Text?+=?"There?are?one?or?more?required?fields?that?are?missing.";
17????????????else?if?(inner?is?ArgumentException)
18????????????{
19????????????????string?paramName?=?((ArgumentException)inner).ParamName;
20????????????????ExceptionDetails.Text?+=?string.Concat("The?",?paramName,?"?value?is?illegal.");
21????????????}
22????????????else?if?(inner?is?ApplicationException)ExceptionDetails.Text?+=?inner.Message;
23????????}
24
25????????//?Indicate?that?the?exception?has?been?handled
26????????e.ExceptionHandled?=?true;
27
28????????//?Keep?the?row?in?edit?mode
29????????e.KeepInEditMode?=?true;
30????}
31}
32
可以看出來(lái),如果UnitPrice小于0,產(chǎn)生的異常,在這里被捕獲了
// UnitPrice小于0,拋出異常
public?partial?class?Northwind
{
????public?partial?class?ProductsDataTable
????{
????????public?override?void?BeginInit()
????????{
????????????this.ColumnChanging?+=?ValidateColumn;
????????}
????????void?ValidateColumn(object?sender,?DataColumnChangeEventArgs?e)
????????{
????????????if?(e.Column.Equals(this.UnitPriceColumn))
????????????{
????????????????if?(!Convert.IsDBNull(e.ProposedValue)?&&?(decimal)e.ProposedValue?<?0)
????????????????{
????????????????????throw?new?ArgumentException("UnitPrice?cannot?be?less?than?zero",?"UnitPrice");
????????????????}
????????????}
????????????else?if?(e.Column.Equals(this.UnitsInStockColumn)?||
????????????????????e.Column.Equals(this.UnitsOnOrderColumn)?||
????????????????????e.Column.Equals(this.ReorderLevelColumn))
????????????{
????????????????if?(!Convert.IsDBNull(e.ProposedValue)?&&?(short)e.ProposedValue?<?0)
????????????????{
????????????????????throw?new?ArgumentException(string.Format("{0}?cannot?be?less?than?zero",?e.Column.ColumnName),e.Column.ColumnName);
????????????????}
????????????}
????????}
????}
}
BLL層面的異常也在這里被捕獲
我們給UpdateProduct重載增加一個(gè)業(yè)務(wù)規(guī)則:禁止把UnitPrice字段的值設(shè)置為超過(guò)原來(lái)的兩倍。為了實(shí)現(xiàn)這一點(diǎn),調(diào)整UpdateProduct重載以使它可以執(zhí)行這個(gè)檢查并且在違反該規(guī)則時(shí)拋出一個(gè)ApplicationException異常。
?//?Make?sure?the?price?has?not?more?than?doubled
11????if?(unitPrice?!=?null?&&?!product.IsUnitPriceNull())
12????????if?(unitPrice?>?product.UnitPrice?*?2)
13??????????throw?new?ApplicationException("When?updating?a?product?price,"?+?"?the?new?price?cannot?exceed?twice?the?original?price.");
BLL引發(fā)的ApplicationException異常在GridView的RowUpdated事件處理程序中被偵測(cè)并處理。
驗(yàn)證控件
對(duì)比上面對(duì)UnitPrice<0的處理,拋出異常,在RowUpdated捕獲;
一般更常用的是增加驗(yàn)證控件。
例如給ProductName的EditItemTemplate添加RequiredFieldValidator驗(yàn)證控件,
RequiredFieldValidator的ControlToValidate屬性為EditProductName。最后,設(shè)置ErrorMessage屬性為“You must provide the product’s name” 并將Text屬性設(shè)置為“*”。
例如給UnitPrice 的EditItemTemplate模板增加CompareValidator驗(yàn)證控件
設(shè)置CompareValidator控件ControlToValidate屬性為T(mén)extBoxID,Operator屬性為GreaterThanEqual,ValueToCompare屬性為 “0”, 并且Type屬性為Currency,ErrorMessage屬性為“The price must be greater than or equal to zero and cannot include the currency symbol”,
ASP.NET包含了一個(gè)總結(jié)控件ValidationSummary control,可以顯示那些檢測(cè)到無(wú)效數(shù)據(jù)的驗(yàn)證控件的ErrorMessage。以文本方式在頁(yè)上某個(gè)位置概述錯(cuò)誤結(jié)果,或者通過(guò)一個(gè)客戶(hù)端消息框。
設(shè)置其ShowSummary屬性為false并設(shè)置ShowMessageBox屬性為true。不需要設(shè)置其他什么屬性。這樣,所有的驗(yàn)證錯(cuò)誤都會(huì)顯示在一個(gè)客戶(hù)端消息框中。
對(duì)驗(yàn)證控件要分組
默認(rèn)情況下,當(dāng)postback發(fā)生時(shí)頁(yè)面上所有的驗(yàn)證都會(huì)生效。顯然,當(dāng)編輯GridView記錄時(shí)我們不希望DetailsView新增功能的驗(yàn)證起作用,
尷尬局面-當(dāng)用戶(hù)在編輯product時(shí)輸入了有效數(shù)據(jù),在點(diǎn)擊更新時(shí)卻由于新增功能中的name和price空白而產(chǎn)生驗(yàn)證錯(cuò)誤。
例如將GridView中CommandField的ValidationGroup屬性則指定為EditValidationControls(直接輸入),GridView EditItemTemplate中可以指定的,ValidationGroup屬性統(tǒng)一設(shè)置為EditValidationControls,驗(yàn)證控件的ValidationGroup屬性設(shè)置為EditValidationControls。
驗(yàn)證組中的驗(yàn)證控件只在有相同ValidationGroup屬性的按鈕產(chǎn)生postback時(shí)才會(huì)進(jìn)行有效性檢測(cè)。
對(duì)于ValidationSummary控件也要改變
由于ValidationSummary控件也擁有ValidationGroup屬性并且只顯示來(lái)自于同一驗(yàn)證組中驗(yàn)證控件的信息。因此,我們需要使用兩個(gè)驗(yàn)證控件,分別作為InsertValidationControls驗(yàn)證組和EditValidationControls驗(yàn)證組:?
ShowSummary="False"?ValidationGroup="EditValidationControls"?/>
<asp:ValidationSummary?ID="ValidationSummary2"?runat="server"?ShowMessageBox="True"
ShowSummary="False"?ValidationGroup="InsertValidationControls"?/>
?
SupplierName列改為DropDownList
只要將其轉(zhuǎn)化為模板,把EditTemplateItem改為DropDownList,設(shè)置綁定的數(shù)據(jù)源,綁定字段。
Product表中的CategoryID 和 SupplierID列允許為NULL,而編輯模板中的下拉列表卻沒(méi)有NULL這一項(xiàng)。所以存在下面兩種問(wèn)題:
1. 用戶(hù)無(wú)法則現(xiàn)在的界面中將某個(gè)product非空的category或supplier設(shè)置為NULL
2. 如果產(chǎn)品的CategoryID 或 SupplierID為NULL,在點(diǎn)擊Edit按鈕時(shí)程序會(huì)拋出異常。這是因?yàn)锽ind()表達(dá)式中CategoryID(或SupplierID)返回NULL值時(shí),SelectedValue無(wú)法找到NULL這一列表項(xiàng)因而拋出異常。?
為了支持CategoryID 和 SupplierID的NULL值,需要為兩個(gè)DropDownList增加一個(gè)NULL值選項(xiàng)。方法是將DropDownList的AppendDataBoundItems屬性設(shè)置為true并手動(dòng)增加一個(gè)值為空字符串的列表項(xiàng)。在ASP.NET的數(shù)據(jù)綁定邏輯中,空字符串將自動(dòng)轉(zhuǎn)換為NULL,NULL值也可以轉(zhuǎn)為空字符串。因此,先元素標(biāo)記大致如下:
<asp:ListItem?Value="">(None)</asp:ListItem>
</asp:DropDownList>
?
開(kāi)放式并發(fā)
見(jiàn)此處
刪除操作的客戶(hù)端確認(rèn)
JavaScript的confirm(string)方法
在一個(gè)模式窗口中顯示那些作為string參數(shù)傳進(jìn)來(lái)的文本,這個(gè)窗口將會(huì)顯示兩個(gè)按鈕-確定(OK)和取消(Cancel)。
根據(jù)點(diǎn)擊不同的按鈕來(lái)返回一個(gè)布爾類(lèi)型值。(如果點(diǎn)擊了OK,返回true;如果點(diǎn)擊Cancel,返回false)
ASP.NET 2.0為為Button,LinkButton,ImageButton新引入的OnClientClick這個(gè)屬性,可以通過(guò)它為客戶(hù)端單擊事件添加客戶(hù)端腳本。
如果onClinetClick=“True"則執(zhí)行單擊,false則放棄。
OnClientClick="return?confirm('Are?you?certain?that?you?want?to?delete?this?product?');">
</asp:LinkButton>
GridView或者DetailsView上內(nèi)置的一些刪除按鈕,怎么處理呢?
方法1. 把他們轉(zhuǎn)化為T(mén)emplate,就變成了按鈕,照上面做
方法2. DataBound事件中進(jìn)行綁定,而且還可以訪問(wèn)字段,增加ProducName
{
????if?(e.Row.RowType?==?DataControlRowType.DataRow)
????{
????????//?reference?the?Delete?LinkButton
????????LinkButton?db?=?(LinkButton)e.Row.Cells[0].Controls[0];?
????????//?Get?information?about?the?product?bound?to?the?row
????????Northwind.ProductsRow?product?=?(Northwind.ProductsRow)?((System.Data.DataRowView)?e.Row.DataItem).Row;?
????????db.OnClientClick?=?string.Format("return?confirm('Are?you?certain?you?want?to?delete?the?{0}?product?');",?
????????product.ProductName.Replace("'",?@"\'"));
????}
}
?
幾點(diǎn)注意:
1.??如果有Select,Delete,則用e.Row.Cells[0].Contorls[2],??
?????因?yàn)樵趧h除按鈕前面有兩個(gè)控件,一個(gè)是編輯按鈕,另一個(gè)是LiteralControl,用來(lái)隔離編輯按鈕和刪除按鈕。
2.??這里用Index訪問(wèn)控件,不變之處是 如果有人增加了Select,Edit,或是改變了GridView的按鈕類(lèi)型,編譯沒(méi)問(wèn)題,點(diǎn)擊就會(huì)異常;而且修改起來(lái)很麻煩;
???? 所以常常把他們轉(zhuǎn)為T(mén)emplate,變?yōu)檎娴陌粹o。
3.? 為什么不用product.ProductName
???? 因?yàn)榭赡苡挟a(chǎn)品名稱(chēng)為Duck's bag,這樣程序產(chǎn)生運(yùn)行時(shí)錯(cuò)誤: error:Expected')'???[拼”return confirm('...‘);"時(shí)候產(chǎn)生錯(cuò)誤]
???? 所以把所有產(chǎn)品名中的' 轉(zhuǎn)為 \'
所以最好的做法是:
1. 把GridView的Delete轉(zhuǎn)為T(mén)emplate,設(shè)置Delete按鈕的ID為“DeleteBtn”
2. RowDataBound事件
????{
????????if?(e.Row.RowType?==?DataControlRowType.DataRow)
????????{
????????????LinkButton?db?=?(LinkButton)e.Row.FindControl("DeleteBtn");
????????????Northwind.ProductsRow?product?=?(Northwind.ProductsRow)((System.Data.DataRowView)e.Row.DataItem).Row;
????????????db.OnClientClick?=?string.Format("return?confirm('Are?you?certain?you?want?to?delete?the?{0}?product?');",
????????????????product.ProductName.Replace("'",@"\'"));
????????}
????}
?
根據(jù)登錄用戶(hù)而定制不同的數(shù)據(jù)修改界面
1. 登陸用戶(hù)不同,綁定不同的DataSouce實(shí)現(xiàn)不同的界面
2. 根據(jù)某項(xiàng)Data,設(shè)置訪問(wèn)權(quán)限,例如被廢棄的產(chǎn)品將是不可編輯的,
Northwind.ProductsRow product = (Northwind.ProductsRow)((System.Data.DataRowView)e.Row.DataItem).Row;
if (product.Discontinued)
{?LinkButton editbutton = (LinkButton)e.Row.FindControl("EditLinkButton");
??editbutton.Visible = false;}
?
轉(zhuǎn)載于:https://www.cnblogs.com/DylanWind/archive/2008/12/22/1359344.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的【ASP.NET Step by Step】之十六至二十三 Inserting, Updating, and Deleting Data的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 怎么改善现有网站
- 下一篇: .NET 指南:捕获并且抛出标准的异常类