【ASP.NET Step by Step】之十六至二十三 Inserting, Updating, and Deleting Data
點擊GridView的刪除,事件觸發過程
GridView控件提供了對行編輯和刪除的內建的支持。配置一個GridView支持刪除需要添加一個刪除按鈕列。當最終用戶點擊某一特定行的刪除按鈕時,引發一次回傳并且GridView執行以下步驟:?
1.????? 對ObjectDataSource的DeleteParameters賦值
2.????? 調用ObjectDataSource的Delete()方法,刪除指定的記錄
3.????? 通過調用它的Select()方法GridView重新綁定到ObjectDataSource??
賦值到DeleteParameters的值是點擊刪除按鈕這一行的DataKeyNames字段的值。因此正確地設置GridView的DataKeyNames屬性是至關重要的。如果缺少了這個,DeleteParameters將在第1步被賦上一個null值,從而在第2步中將不會導致刪除任何記錄。
點擊GridView的編輯,事件觸發過程
當最終用戶點擊特定一行的編輯按鈕時,引發一次回傳并且GridView執行以下步驟:
??
?????2.?通過調用它的Select()方法,GridView重新綁定自己到ObjectDataSource
?????3.?與EditItemIndex相匹配的行呈現為編輯模式。在此模式下,Edit按鈕替換為Update和Cancel按鈕,并且那些ReadOnly屬性為False的綁定列呈現為
?????????TextBox服務器控件,這些TextBox的Text屬性被賦值為相應的數據字段的值。
?
到這里HTML標記被返回到瀏覽器,允許最終用戶可以修改行數據。當用戶點擊保存按鈕,再次發生一次回傳,并且GridView執行以下幾個步驟:
?????1.?ObjectDataSource的UpdateParameters的值被賦值為最終用戶在GridView的編輯界面輸入的值
?????2.?調用ObjectDataSource的Update()方法,更新指定的記錄
?????3.?通過調用Select()方法,GridView重新綁定自己到ObjectDataSource
GridView的DataKeyNames屬性指定的主鍵的值在第1步中賦值到UpdateParameters,反之非主鍵的值來自當前編輯行的TextBox服務器控件。如果DataKeyNames遺漏了,那么UpdateParameters主鍵的值在第1步中將被賦上一個空值,然后轉入第2步中將不會導致任何記錄的更新。
也可以這樣說:基于GridView的DataKeyNames里的主鍵值和GridView里的值,組成ObjectDataSOurce的UpdateParameters
如圖所示,更新數據,觸發一連串的Pre-和Post-事件
注意:
我們創建的BLL層UpdateProduct方法是接受所有參數的,所以如果我們的GridView里綁定的列少了一些,
編輯的時候,這些沒綁定的非只讀列會被ObjectDataSource自做主張的置為NULL傳給BLL層;
因此如果GridView少了ProductName,因為它是要求非空的,所以Update的時候就產生了異常。
所以,我們綁定幾列,就應該在BLL層重載UpdateProduct(幾列)方法。
例如,我們在BLL重載了帶三個參數的UpdateProduct方法,并且指定ObjectDataSource控件的Update方法為三個參數的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>
這時,如果GridView是所有列,那么ObjectDataSource則會調用能接受這些參數的方法重載,
而不顧ObjectDataSource的聲明標記指定只接受3個輸入參數的事實。
而如果GridView含有4個列,則會在試圖保存時引發一個異常
could not find a non-generic method 'UpdateProduct' that has parameters: ProductName, UnitPrice, ProductID, QuantityPerUnit.
注意Added by GridView
Pre級事件處理UnitPrice格式問題
UnitPrice的顯示格式是{0:C}, 如果想讓編輯時候也顯示這個狀態,要設置綁定列的ApplyFormatInEditMode屬性為true,
這樣設置后產生的新問題是當GridView嘗試把用戶提供的值賦值到ObjectDataSource的UpdateParameters集合,它無法把UnitPrice字符串“$19.00”轉換成參數要求的decimal類型,
解決方法:
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;
?}
}
它包含一個NewValues字典,當中的每一個屬性保存著用戶輸入的值,
e.Cancel=true;? 說明當用戶忘記輸入UnitPrice,則顯示提示信息,并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") %>?? 雙向綁定,
用戶點擊編輯按鈕,Bind()綁定數據到模版(TextBox),
點擊Update按鈕,通過Bind()指定的數據字段的值被回傳到ObjectDataSource的UpdateParameters
<%# Eval("dataField") %>?? 單向綁定,
僅僅在綁定數據到模版時取得數據字段的值,但并不會在回傳時將用戶輸入的值返回到數據源控件的參數
Post級事件用于異常處理
1. 如果數據庫不正常運作,則在試圖連接數據庫時通過TableAdapter拋出一個SqlException異常。
2. DAL層異常,遺漏了ProductName值則引發拋出一個NoNullAllowedException異常,因為ProdcutsRow類的ProductName屬性設置了它的AllowDBNull屬性為false(這句話好像有點問題)
3. BLL層異常,
數據庫這樣的設置 .NET在創建DataSet時做了什么呢?
.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;
//
}
夠復雜吧
還是回到主題,沒有任何的動作,這些異常都會從數據訪問層冒出到業務邏輯層,然后到ASP.NET頁面,最后到ASP.NET運行時
用戶是不希望看到這樣的東西的。人性化的方法是我們提前處理這些異常,給用戶友好的界面。
ObjectDataSource 和數據Web控件的post級事件都提供了發現并不讓它出現在ASP.NET運行時的方法
下面的代碼實現了上述的目的:
?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
可以看出來,如果UnitPrice小于0,產生的異常,在這里被捕獲了
// 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重載增加一個業務規則:禁止把UnitPrice字段的值設置為超過原來的兩倍。為了實現這一點,調整UpdateProduct重載以使它可以執行這個檢查并且在違反該規則時拋出一個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引發的ApplicationException異常在GridView的RowUpdated事件處理程序中被偵測并處理。
驗證控件
對比上面對UnitPrice<0的處理,拋出異常,在RowUpdated捕獲;
一般更常用的是增加驗證控件。
例如給ProductName的EditItemTemplate添加RequiredFieldValidator驗證控件,
RequiredFieldValidator的ControlToValidate屬性為EditProductName。最后,設置ErrorMessage屬性為“You must provide the product’s name” 并將Text屬性設置為“*”。
例如給UnitPrice 的EditItemTemplate模板增加CompareValidator驗證控件
設置CompareValidator控件ControlToValidate屬性為TextBoxID,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包含了一個總結控件ValidationSummary control,可以顯示那些檢測到無效數據的驗證控件的ErrorMessage。以文本方式在頁上某個位置概述錯誤結果,或者通過一個客戶端消息框。
設置其ShowSummary屬性為false并設置ShowMessageBox屬性為true。不需要設置其他什么屬性。這樣,所有的驗證錯誤都會顯示在一個客戶端消息框中。
對驗證控件要分組
默認情況下,當postback發生時頁面上所有的驗證都會生效。顯然,當編輯GridView記錄時我們不希望DetailsView新增功能的驗證起作用,
尷尬局面-當用戶在編輯product時輸入了有效數據,在點擊更新時卻由于新增功能中的name和price空白而產生驗證錯誤。
例如將GridView中CommandField的ValidationGroup屬性則指定為EditValidationControls(直接輸入),GridView EditItemTemplate中可以指定的,ValidationGroup屬性統一設置為EditValidationControls,驗證控件的ValidationGroup屬性設置為EditValidationControls。
驗證組中的驗證控件只在有相同ValidationGroup屬性的按鈕產生postback時才會進行有效性檢測。
對于ValidationSummary控件也要改變
由于ValidationSummary控件也擁有ValidationGroup屬性并且只顯示來自于同一驗證組中驗證控件的信息。因此,我們需要使用兩個驗證控件,分別作為InsertValidationControls驗證組和EditValidationControls驗證組:?
ShowSummary="False"?ValidationGroup="EditValidationControls"?/>
<asp:ValidationSummary?ID="ValidationSummary2"?runat="server"?ShowMessageBox="True"
ShowSummary="False"?ValidationGroup="InsertValidationControls"?/>
?
SupplierName列改為DropDownList
只要將其轉化為模板,把EditTemplateItem改為DropDownList,設置綁定的數據源,綁定字段。
Product表中的CategoryID 和 SupplierID列允許為NULL,而編輯模板中的下拉列表卻沒有NULL這一項。所以存在下面兩種問題:
1. 用戶無法則現在的界面中將某個product非空的category或supplier設置為NULL
2. 如果產品的CategoryID 或 SupplierID為NULL,在點擊Edit按鈕時程序會拋出異常。這是因為Bind()表達式中CategoryID(或SupplierID)返回NULL值時,SelectedValue無法找到NULL這一列表項因而拋出異常。?
為了支持CategoryID 和 SupplierID的NULL值,需要為兩個DropDownList增加一個NULL值選項。方法是將DropDownList的AppendDataBoundItems屬性設置為true并手動增加一個值為空字符串的列表項。在ASP.NET的數據綁定邏輯中,空字符串將自動轉換為NULL,NULL值也可以轉為空字符串。因此,先元素標記大致如下:
<asp:ListItem?Value="">(None)</asp:ListItem>
</asp:DropDownList>
?
開放式并發
見此處
刪除操作的客戶端確認
JavaScript的confirm(string)方法
在一個模式窗口中顯示那些作為string參數傳進來的文本,這個窗口將會顯示兩個按鈕-確定(OK)和取消(Cancel)。
根據點擊不同的按鈕來返回一個布爾類型值。(如果點擊了OK,返回true;如果點擊Cancel,返回false)
ASP.NET 2.0為為Button,LinkButton,ImageButton新引入的OnClientClick這個屬性,可以通過它為客戶端單擊事件添加客戶端腳本。
如果onClinetClick=“True"則執行單擊,false則放棄。
OnClientClick="return?confirm('Are?you?certain?that?you?want?to?delete?this?product?');">
</asp:LinkButton>
GridView或者DetailsView上內置的一些刪除按鈕,怎么處理呢?
方法1. 把他們轉化為Template,就變成了按鈕,照上面做
方法2. DataBound事件中進行綁定,而且還可以訪問字段,增加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("'",?@"\'"));
????}
}
?
幾點注意:
1.??如果有Select,Delete,則用e.Row.Cells[0].Contorls[2],??
?????因為在刪除按鈕前面有兩個控件,一個是編輯按鈕,另一個是LiteralControl,用來隔離編輯按鈕和刪除按鈕。
2.??這里用Index訪問控件,不變之處是 如果有人增加了Select,Edit,或是改變了GridView的按鈕類型,編譯沒問題,點擊就會異常;而且修改起來很麻煩;
???? 所以常常把他們轉為Template,變為真的按鈕。
3.? 為什么不用product.ProductName
???? 因為可能有產品名稱為Duck's bag,這樣程序產生運行時錯誤: error:Expected')'???[拼”return confirm('...‘);"時候產生錯誤]
???? 所以把所有產品名中的' 轉為 \'
所以最好的做法是:
1. 把GridView的Delete轉為Template,設置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("'",@"\'"));
????????}
????}
?
根據登錄用戶而定制不同的數據修改界面
1. 登陸用戶不同,綁定不同的DataSouce實現不同的界面
2. 根據某項Data,設置訪問權限,例如被廢棄的產品將是不可編輯的,
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;}
?
轉載于:https://www.cnblogs.com/DylanWind/archive/2008/12/22/1359344.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的【ASP.NET Step by Step】之十六至二十三 Inserting, Updating, and Deleting Data的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么改善现有网站
- 下一篇: 蓝桥杯第八届省赛JAVA真题----9数