原程序,可以從下面地址下載:http://download.cnblogs.com/insus/ASPDOTNET/Ref_Org.rar
數據庫SQL Server 2008 R2,數據名為[Demo],它有四張表[UnitCode1], [UnitCode2], [UnitCode3], [UnitCode4],每個表有幾個字段[Unit1~4],[Description],[CreateBy],[CreateDate],[UpdateBy],[UpdateDate],此四個表的主鍵分別為[Unit1],[Unit2],[Unit3]和[Unit4],其余字段名稱四張表都一樣。數據庫還有各個表的相關的存儲過程。
程序中有一個接口,是為了設置網頁標題。數據庫四張表對應的類別,程序應用了母版,有5個網頁,Default.aspx, UnitCode1.aspx, UnitCode2.aspx, UnitCode3.aspx和UnitCode4.aspx 。每個網頁分別也是對各自的表進行添加,顯示,更新以及刪除記錄的功能。
其實,這就是一個小程序,基本的功能都齊全。
-------------------------------------------------------------------------------------------------------------------------------------
根據此篇博文,我們來學習一下繼承。
打開程序,我們會到四個網頁的cs代碼與四個類別98%相同,只是每個表的主鍵名,以及存儲過程名稱等不一樣。繼承是把相同而共用的屬性,方法,函數放置於父類中,這樣繼承的類,就是使用到這些共用的protected或public的程序塊。盡可能簡化,變化的地方盡量未端化去維護。
我們創建一個父類,比如叫BaseUnitCode.cs吧。
首先我們對比四個類別中,屬性部分,只有
private string _Unit1; public string Unit1 { get { return _Unit1; } set { _Unit1 = value; } }
不相同,因此我們就把它留在原本的類別中。其余的屬性都移至父類BaseUnitCode,還有一句,就是邏輯處理的類實例
BusinessBase objBusinessBase = new BusinessBase();
在每個類別中也一樣,因此也移至父類,下面是剛才重構好的父類。
接下來,我看到每個類別的GetAll(), Insert(), Update(), Delete()方法中,只有一些參數名,參數值,以及存儲過程名有差異。下面是每個方法移至父類之后,作相應的修改:
GetAll()方法:
Comment out的代碼,就是移到父類的代碼,需要修改方法以及修飾符為Protected,這因為只是想讓繼承這個父類的類訪問到即可。而剛才上面屬性,還是保持原樣“public”。
由於存儲程名稱在每個類別都不一樣,因此方法名改為帶一個參數的方法。
Insert()方法:
重構之后,Insert()方法被改為帶三個參數的方法,這樣解決傳入主鍵,主鍵值,以及Insert的存儲過程不一樣的問題。
同樣,Update():
Ok,我們的父類重構好了:

using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Web; /// <summary> /// Summary description for BaseUnitCode /// </summary> namespace Insus.NET { public class BaseUnitCode { private string _Description; private string _CreateBy; private string _UpdateBy; public string Description { get { return _Description; } set { _Description = value; } } public string CreateBy { get { return _CreateBy; } set { _CreateBy = value; } } public string UpdateBy { get { return _UpdateBy; } set { _UpdateBy = value; } } BusinessBase objBusinessBase = new BusinessBase(); public BaseUnitCode() { // // TODO: Add constructor logic here // } protected DataTable GetAll(string procedureName) { return objBusinessBase.GetDataToDataSet(procedureName).Tables[0]; } protected void Insert(string paramName, string paramValue, string procedurename) { Parameter[] parameter = { new Parameter (paramName,SqlDbType.NVarChar,-1,paramValue), new Parameter ("@Description",SqlDbType.NVarChar,-1,_Description), new Parameter ("@CreateBy",SqlDbType.NVarChar,-1,_CreateBy) }; objBusinessBase.ExecuteProcedure(procedurename, parameter); } protected void Update(string paramName, string paramValue, string procedurename) { Parameter[] parameter = { new Parameter (paramName,SqlDbType.NVarChar,-1,paramValue), new Parameter ("@Description",SqlDbType.NVarChar,-1,_Description), new Parameter ("@UpdateBy",SqlDbType.NVarChar,-1,_UpdateBy) }; objBusinessBase.ExecuteProcedure(procedurename, parameter); } protected void Delete(string paramName, string paramValue, string procedurename) { Parameter[] parameter = { new Parameter (paramName,SqlDbType.NVarChar,-1,paramValue) }; objBusinessBase.ExecuteProcedure(procedurename, parameter); } } }
接下來,我們需要在每一個UnitCode1.cs至UnitCode4.cs分別繼承這個父類,下面只演法UnitCode1.cs,其余的UnitCode2至UnitCode4參考就是了。
下面是重構好的類別:

using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Web; /// <summary> /// Summary description for UnitCode1 /// </summary> namespace Insus.NET { public class UnitCode1 : BaseUnitCode { private string _Unit1; public string Unit1 { get { return _Unit1; } set { _Unit1 = value; } } public UnitCode1() { // // TODO: Add constructor logic here // } public DataTable GetAll() { return GetAll("usp_UnitCode1_GetAll"); } public void Insert() { Insert("@Unit1", _Unit1, "usp_UnitCode1_Insert"); } public void Update() { Update("@Unit1", _Unit1, "usp_UnitCode1_Update"); } public void Delete() { Delete("@Unit1", _Unit1, "usp_UnitCode1_Delete"); } } }

using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Web; /// <summary> /// Summary description for UnitCode1 /// </summary> namespace Insus.NET { public class UnitCode2 : BaseUnitCode { private string _Unit2; public string Unit2 { get { return _Unit2; } set { _Unit2 = value; } } public UnitCode2() { // // TODO: Add constructor logic here // } public DataTable GetAll() { return GetAll("usp_UnitCode2_GetAll"); } public void Insert() { Insert("@Unit2", _Unit2, "usp_UnitCode2_Insert"); } public void Update() { Update("@Unit2", _Unit2, "usp_UnitCode2_Update"); } public void Delete() { Delete("@Unit2", _Unit2, "usp_UnitCode2_Delete"); } } }

using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Web; /// <summary> /// Summary description for UnitCode3 /// </summary> namespace Insus.NET { public class UnitCode3 : BaseUnitCode { private string _Unit3; public string Unit3 { get { return _Unit3; } set { _Unit3 = value; } } public UnitCode3() { // // TODO: Add constructor logic here // } public DataTable GetAll() { return GetAll("usp_UnitCode3_GetAll"); } public void Insert() { Insert("@Unit3", _Unit3, "usp_UnitCode3_Insert"); } public void Update() { Update("@Unit3", _Unit3, "usp_UnitCode3_Update"); } public void Delete() { Delete("@Unit3", _Unit3, "usp_UnitCode3_Delete"); } } }

using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Web; /// <summary> /// Summary description for UnitCode1 /// </summary> namespace Insus.NET { public class UnitCode4 : BaseUnitCode { private string _Unit4; public string Unit4 { get { return _Unit4; } set { _Unit4 = value; } } public UnitCode4() { // // TODO: Add constructor logic here // } public DataTable GetAll() { return GetAll("usp_UnitCode4_GetAll"); } public void Insert() { Insert("@Unit4", _Unit4, "usp_UnitCode4_Insert"); } public void Update() { Update("@Unit4", _Unit4, "usp_UnitCode4_Update"); } public void Delete() { Delete("@Unit4", _Unit4, "usp_UnitCode4_Delete"); } } }
類別重構完成,接下來,我們對UnitCode1.aspx.cs至UnitCode4.aspx.cs進行重構。因為這四個頁面的類,也有很多相同的代碼。重構之前,先創建一個頁面的基類,暫叫它為BasePage,此類別繼承了
每個.aspx.cs繼承剛才寫的BasePage類,把InsusJsUtility objInsusJsUtility = new InsusJsUtility(); 這句拿掉,並移至BasePage中,根據繼承的精神,它足夠條件移了。
移至BasePage之后,需要添加修飾符protected,這樣每個.aspx.cs才可以訪問得到。

using System; using System.Collections.Generic; using System.Linq; using System.Web; /// <summary> /// Summary description for BasePage /// </summary> namespace Insus.NET { public class BasePage : System.Web.UI.Page { protected InsusJsUtility objInsusJsUtility = new InsusJsUtility(); } }
接下來,我們眼睛注意到每個.aspx.cs的Data_Binding()方法中,均有此一句:
((ISetValable)this.Master).SetValue("單位碼X");
然后在每個.aspx.cs中,拿掉((ISetValable)this.Master).SetValue("單位碼X"); 這句,並改為如下圖高亮語句。
到此為止,Insus.NET暫停對.cs代碼重構,轉而看到Html代碼。如下圖中的插入記錄代碼,在四處個網頁中每個.aspx都是相同的,因此Insus.NET對這些重構。
對這部分的重構,只有創建用戶控件(ascx),然后搬移過去,完成之后,再把這個用戶控件拉至網頁.aspx中:

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="InsertForm.ascx.cs" Inherits="Sys_InsertForm" %> <table class="table"> <tr class="tableRow"> <td class="tableCell" style="width: 35%;">單位碼</td> <td class="tableCell">說明</td> <td style="width: 12%; text-align: center;" class="tableCell">操作</td> </tr> <tr> <td class="tableCell"> <asp:TextBox ID="TextBoxUnitCode" runat="server" CssClass="textbox" BackColor="#ffff6f"></asp:TextBox> <asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" ControlToValidate="TextBoxUnitCode" Display="none" ErrorMessage="單位碼必須填寫。" ValidationGroup="GeneralInsert"></asp:RequiredFieldValidator> </td> <td class="tableCell"> <asp:TextBox ID="TextBoxDescription" runat="server" CssClass="textbox"></asp:TextBox> </td> <td style="width: 12%;" class="tableCell"> <asp:ValidationSummary ID="ValidationSummary1" runat="server" EnableClientScript="true" ShowMessageBox="true" ShowSummary="false" ValidationGroup="GeneralInsert" /> <asp:Button ID="ButtonCreate" runat="server" OnClick="ButtonCreate_Click" Text="創建" ValidationGroup="GeneralInsert" /> </td> </tr> </table>
由於每個.aspx創建事件不一樣,為了保持原有在.aspx.cs的事件,Insus.NET決定在用戶控件中再public Click 事件。
上圖中,還寫兩個屬性,分別是兩個文本框的屬性,這是為了讓.aspx還能與用戶控件的兩個文本框的交互。
用戶控件重構好之后,當然需要拉至網頁中去,有一個地方是需要注意的,在用戶控件,還要寫上OnClick事件,OnClick="ButtonCreate_Click":
在.aspx.cs代碼頁中,一些代碼需要異動,參考下圖高亮位置:
改程序,就得一步一個腳印,現在我們把目光放在每個.aspx的GridView控件上,它是顯示記錄,編輯記錄以及刪除等功能集成。有很高的相似度。只是Gridview的ID,DataKeyNames,以及OnRowEditing,OnRowCancelingEdit,OnRowUpdating,OnRowDeleting事件名稱不一樣,最后是綁定主鍵時,也不一樣:
<ItemTemplate> <%# Eval("UnitX") %> </ItemTemplate>
這部分重構,相似度,但好象又很具有獨立性,無法分開。現在Insus.NET決定對這些ID以及事件名改為一樣。刪除箭頭所指的數字:
改完之后,如下代碼一樣,Insus.NET只列了個網頁,如Unitcode4(部分):
<asp:GridView ID="GridViewUnitCode" runat="server" DataKeyNames="Unit4" AutoGenerateColumns="false" ShowHeader="false" CellPadding="2" CellSpacing="0" Width="100%" BorderWidth="1px" BorderColor="#c0c0c0" BorderStyle="solid" HeaderStyle-Height="25" RowStyle-Height="25" HeaderStyle-BackColor="#efebde" OnRowEditing="GridViewUnitCode_RowEditing" OnRowCancelingEdit="GridViewUnitCode_RowCancelingEdit" OnRowUpdating="GridViewUnitCode_RowUpdating" OnRowDeleting="GridViewUnitCode_RowDeleting"> <Columns> <asp:TemplateField> <ItemStyle BorderStyle="solid" BorderWidth="1px" BorderColor="#c0c0c0" Width="35%" /> <ItemTemplate> <%# Eval("Unit4") %> </ItemTemplate> </asp:TemplateField>
當然在每個UnitCode1~3.aspx.cs的事件中,也應該修改,參考下圖,把箭頭的數據全刪除。
重名命重構,往往改動的地方都較多。全部改完之后,所有UnitCode1~4.aspx只差下圖高亮位置的差異了:
怎樣解決這些差異,它是一個表字段,而且是主鍵。動態產生或是加載是否可行,想到了,行動就是了。在GridView中,去掉DataKeyNames="Unit1"屬性。在顯示ItemTemple中,改為一個標簽,並為GridView添加一個事件 OnRowDataBound="GridViewUnitCode_RowDataBound"。
在.aspx.cs中,添加一個變量,四個網頁的變量值不同,分別為
string _DataKeyName = "Unit1";
string _DataKeyName = "Unit2";
string _DataKeyName = "Unit3";
string _DataKeyName = "Unit4";
參考下面動畫:
程序經此一改,每個頁面的html又一樣了。所以我們可以把其中一頁的GridView html代碼塊搬移至一個用戶控件之內。創建一個用戶控件:

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="OperationForm.ascx.cs" Inherits="Sys_OperationForm" %> <asp:GridView ID="GridViewUnitCode" runat="server" AutoGenerateColumns="false" ShowHeader="false" CellPadding="2" CellSpacing="0" Width="100%" BorderWidth="1px" BorderColor="#c0c0c0" BorderStyle="solid" HeaderStyle-Height="25" RowStyle-Height="25" HeaderStyle-BackColor="#efebde" OnRowEditing="GridViewUnitCode_RowEditing" OnRowCancelingEdit="GridViewUnitCode_RowCancelingEdit" OnRowUpdating="GridViewUnitCode_RowUpdating" OnRowDeleting="GridViewUnitCode_RowDeleting" OnRowDataBound="GridViewUnitCode_RowDataBound"> <Columns> <asp:TemplateField> <ItemStyle BorderStyle="solid" BorderWidth="1px" BorderColor="#c0c0c0" Width="35%" /> <ItemTemplate> <asp:Label ID="LabelUnitCode" runat="server" Text=""></asp:Label> </ItemTemplate> </asp:TemplateField> <asp:TemplateField> <ItemStyle BorderStyle="solid" BorderWidth="1px" BorderColor="#c0c0c0" /> <ItemTemplate> <%# Eval("Description") %> </ItemTemplate> <EditItemTemplate> <asp:TextBox ID="TextBoxDescription" runat="server" Text='<%# Eval("Description") %>' CssClass="textbox"></asp:TextBox> </EditItemTemplate> </asp:TemplateField> <asp:TemplateField> <ItemStyle BorderStyle="solid" BorderWidth="1px" BorderColor="#c0c0c0" Width="8%" /> <ItemTemplate> <asp:Button ID="ButtonEdit" runat="server" Text="編輯" CommandName="Edit" CausesValidation="false" /> </ItemTemplate> <EditItemTemplate> <asp:ValidationSummary ID="ValidationSummary2" runat="server" EnableClientScript="true" ShowMessageBox="true" ShowSummary="false" ValidationGroup="GrieviewUpdate" /> <asp:Button ID="ButtonUpdate" runat="server" Text="更新" CommandName="Update" ValidationGroup="GrieviewUpdate" /> <asp:Button ID="ButtonCancel" runat="server" Text="取消" CommandName="Cancel" CausesValidation="false" /> </EditItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="刪除"> <ItemStyle BorderStyle="solid" BorderWidth="1px" BorderColor="#c0c0c0" Width="4%" /> <ItemTemplate> <asp:Button ID="ButtonDelete" runat="server" Text="刪除" CommandName="Delete" CausesValidation="false" /> <ajaxToolkit:ConfirmButtonExtender ID="ConfirmButtonExtender1" runat="server" TargetControlID="ButtonDelete" ConfirmText="確認刪除記錄?"> </ajaxToolkit:ConfirmButtonExtender> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>
在OperationForm.ascx.cs中,需要處理幾個問題,一是GridView的事件,主鍵,還是數據綁定的問題等。現在Insus.NET先解決GridView事件:
宣告幾個事件,除了GridViewUnitCode_RowDataBound(object sender, GridViewRowEventArgs e)無需處理,因為它與頁沒有任何交互,只是為GridView顯示數據而已。
下面是我們要解決主鍵,數據綁定,還有在GrieView顯示主鍵的問題,在用戶控件中,寫一個只寫屬性,因為只需要為用戶控件寫入屬性,不必為從用戶控件獲取值。
private string _DataKeyName; public string DataKeyName { set { _DataKeyName = value; } }
接下來,我們又需要去到每一個.aspx.cs中,為剛才的寫好的屬性賦值,你將看到下圖高亮代碼行,這樣子,在每個網頁運行時,就把網頁的主鍵字符名稱傳至用戶控件內。
處理Gridview顯示主鍵時,把下面的方法全搬至用戶控件中,其余網頁相同的事件刪除。
protected void GridViewUnitCode_RowDataBound(object sender, GridViewRowEventArgs e) { if (e.Row.RowType != DataControlRowType.DataRow) return; var drv = e.Row.DataItem as DataRowView; if (e.Row.FindControl("LabelUnitCode") != null) { var lbl = e.Row.FindControl("LabelUnitCode") as Label; lbl.Text = drv[_DataKeyName].ToString(); } }
為了讓.aspx.cs能與用戶控件更好的交互,需要在站點創建一個接口:

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI.WebControls; /// <summary> /// Summary description for IGridViewControlable /// </summary> namespace Insus.NET { public interface IGridViewControlable { GridView GetGridViewControl(); } }
在用戶控件OperationForm.ascx.cs實作它。
在每一個.aspx.cs代碼頁中,寫一個get屬性:

GridView GridViewUnitCode { get { return ((IGridViewControlable)this.OperationForm1).GetGridViewControl(); } }
寫到此,還沒有完畢,精彩還在后頭。我們再寫一個基類,此基將為前面寫好的兩個用戶控件繼承。寫用戶控件的基類,跟頁面的基類完全一樣。看到否,這基類是繼承了System.Web.UI.UserControl:
基類寫好,去分別打開以前寫好的兩個用戶控件,繼承這個用戶控件的基類:
然后把在InsertForm.ascx.cs中下面代碼移至BaseUserControl.cs控件中:
public event EventHandler Click; protected void ButtonCreate_Click(object sender, EventArgs e) { if (Click != null) { Click(this, e); } }
相同的情況,把OperationForm.cs中下面代碼也移至BaseUserControl.cs控件中,移至之后,需要在這個基類的用戶控件中,引用命名空間 using System.Web.UI.WebControls;

public event GridViewEditEventHandler RowEditing; public event GridViewCancelEditEventHandler RowCancelingEdit; public event GridViewUpdateEventHandler RowUpdating; public event GridViewDeleteEventHandler RowDeleting; protected void GridViewUnitCode_RowEditing(object sender, GridViewEditEventArgs e) { if (RowEditing != null) RowEditing(sender, e); } protected void GridViewUnitCode_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e) { if (RowCancelingEdit != null) RowCancelingEdit(sender, e); } protected void GridViewUnitCode_RowUpdating(object sender, GridViewUpdateEventArgs e) { if (RowUpdating != null) RowUpdating(sender, e); } protected void GridViewUnitCode_RowDeleting(object sender, GridViewDeleteEventArgs e) { if (RowDeleting != null) RowDeleting(sender, e); }
也就是說,當多用戶控件中共用的屬性,方法或是函數,也可以寫在基類中。
接下來,我們還看到每個.aspx.cs還在一段相同的代碼:
GridView GridViewUnitCode { get { return ((IGridViewControlable)this.OperationForm1).GetGridViewControl(); } }
Insus.NET也想把它移至基類BasePage.cs中去,直接cut and paste,它會提示找不到this.OperationForm1這個物件。不管怎樣,出錯就出錯,移過去再說,其余的刪除。解決問題,還是使用接口吧:

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; /// <summary> /// Summary description for IUserControlable /// </summary> namespace Insus.NET { public interface IUserControlable { UserControl GetUserControl(); } }
然后每個UnitCode1~4.aspx.cs均實作這個接口,下僅在一個類演示:
我們打開BasePage基類,看下動畫,很簡單把剛才找不到物件的問題解決了:
ok,此博文到此為止,望看過的網友,能從中學習或溫習到繼承知識,了解到類別與父類,網頁與基類頁,用戶控件與用戶控件基類,還有的是網頁與用戶控件之間的交互通訊等。
最終重構好的程序,可以下載與博文開頭的原程序對比。
http://download.cnblogs.com/insus/ASPDOTNET/Ref_Org_inhert.rar