更正:
不好意思,昨天晚上思路有點混亂。有幾個前提忘記說明了,現在補充一下。
1、縮小範圍。按照由簡到難的思路,這裡先讨論最簡單的添加資料的情況。就是單表的添加和修改;這裡讨論的是webform的情況。
2、第一步抽象是針對資料通路層的抽象。
如果我沒有了解錯的話,現在大多數人的做法是:有一個表(或者幾個有關聯的表)在資料層裡就要有一個“函數”與之對應,
如果采用的是SQL語句的方式的話,那麼函數的内筒就是組合SQL語句的代碼,
如果采用的是存儲過程的方式的話,那麼函數的内筒就是給存儲過程的參數指派。
而我這裡隻需要一個函數就可以了。這個函數是共用的,是針對所有表的。再準确點說:SQL語句對應兩個函數(一個添加資料、一個修改資料),存儲過程對應一個函數(是添加還是修改在存儲過程内部判斷)。存儲過程的方式在這裡沒有說明。
3、在這裡的代碼我想寫的盡量簡單,寫多了就會影響主體,是以這裡隻寫了主要代碼。
4、如果您有時間的話,您可以拿我的這段代碼(實作添加修改資料功能)和您自己寫的類似的功能的代碼對應一下,先看一下代碼量,然後再看一下如果要增加字段(比如加一個新聞字數),需要修改的代碼的數量。
5、第二步抽象是針對後置代碼的,也可以叫做邏輯層吧。第三步抽象是針對UI的。
6、我的思路是:用簡單的代碼實作複雜的功能(客戶的要求)!
為了便于說明,先舉個實際的例子吧。比如要實作一個釋出資訊(比如新聞)的功能,說白了就是添加資料。
我的做法是先設計資料庫,然後寫.aspx (UI),然後寫.aspx.cs(後置代碼),然後就完成了。
設計資料庫:表名叫做 news ,有一下幾個字段
NewsID
int
4
主鍵自增
Title
nvarchar
50
标題
Kind
int
新聞分類
Editor
20
編輯
Content
ntext
16
新聞内容
NewsAdd.aspx 添加資料的頁面(UI)
<form id="Form1" method="post" runat="server">
<TABLE id="Table1" cellSpacing="1" cellPadding="1" width="400" align="center" border="1">
<TR>
<TD align="right">标題:</TD>
<TD>
<hbs:HBSTextBox id="Txt_Title" runat="server" Columns="50"></hbs:HBSTextBox></TD>
</TR>
<TD align="right">類别:</TD>
<hbs:HBSDropDownList id="Lst_Kind" runat="server" Width="112px"></hbs:HBSDropDownList></TD>
<TD align="right">作者:</TD>
<hbs:HBSTextBox id="Txt_Editor" runat="server" Columns="50"></hbs:HBSTextBox></TD>
<TD align="right">内容:</TD>
<hbs:HBSTextBox id="Txt_Content" runat="server" TextMode="MultiLine" Rows="5" Columns="50"></hbs:HBSTextBox></TD>
</TABLE>
<hr color="#39689f">
<TABLE cellSpacing="1" cellPadding="1" width="100%" align="center" border="0">
<TD align="center">&nbsp;
<asp:Button id="Btn_Save" runat="server" Width="70px" Text=" 确 定 "></asp:Button>&nbsp;<INPUT id="Btn_Return" type="button" value=" 返 回 " onclick="myEsc()"></TD>
</TD></TR>
</form>
可以暫時把 hbs:HBSTextBox 當作一般的 TextBox 看待,HBSTextBox 是我繼承了TextBox寫的一個自定義伺服器控件。
後置代碼(可以看作邏輯層吧)
private void Page_Load(object sender, System.EventArgs e)
{
// 在此處放置使用者代碼以初始化頁面
Response.Cache.SetNoStore();
初始化#region 初始化
//給按鈕加前台的驗證事件
this.Btn_Save.Attributes.Add("onclick","return myCheck();");
if (!Page.IsPostBack)
//先幫定下拉清單框
BindList();
switch (Kind )
case "2":
//準備修改資料,修改用
ShowData();
break;
}
}
#endregion
}
儲存資料#region 儲存資料
private void Btn_Save_Click(object sender, System.EventArgs e)
//儲存成功後關閉視窗
if (SaveData())
Functions.PageRegisterJavascript(Page,"myReturn()");
#endregion
實作儲存資料的函數#region 實作儲存資料的函數
private bool SaveData()
string[] str1 = new string[4];
定義字段#region 定義字段
str1[0] = "Title";
str1[1] = "Kind";
str1[2] = "Editor";
str1[3] = "Content";
string[] str = new string[4];
資料#region 資料
str[0] = this.Txt_Title.TextTrimNone; //Title;
str[1] = this.Lst_Kind.SelectedValue //Kind;
str[2] = this.Txt_Editor .TextTrimNone; //Auther;
str[3] = this.Txt_Content.TextTrimNone; //Content;
驗證資料#region 驗證資料
//代碼略。寫多了會影響這裡的主題
儲存資料#region 儲存資料
switch (Kind )
case "1":
//添加時驗證是否有相同的記錄,代碼略。寫多了會影響這裡的主題
//添加資料
dal.InsertDataStr("News",str1,str);
break;
case "2":
//修改時驗證是否有相同的記錄,代碼略。寫多了會影響這裡的主題
//修改資料
dal.UpdateData("News",str1,str," NewsID=" + DataID);
檢查是否出現異常#region 檢查是否出現異常
if (dal.ErrorMsg.Length > 2)
Functions.PageRegisterAlert(Page,"儲存資料的時候出現意外情況,請與管理者聯系!");
return false;
Functions.PageRegisterAlert(Page,"儲存成功!");
return true;
這樣呢就完成了添加資料的功能。什麼,您問資料通路層呢?對于我來說資料通路層是通用的,也就是标題裡說的第一步抽象!
還是貼出來吧
#region 查詢語句的方式添加、修改資料
/// <summary>
/// 添加記錄。傳入表名,字段數組,值數組,傳回新生成記錄的ID
/// </summary>
/// <param name="TableName">要添加記錄的表的名稱</param>
/// <param name="ziduan">字段名數組</param>
/// <param name="msg">字段對應的值的數組</param>
/// <returns></returns>
public string InsertDataStr(string TableName , string[] ziduan , string[] msg )
//添加資料 傳回新添加的ID
System.Text.StringBuilder SQL = new System.Text.StringBuilder(300);
SQL.Append("insert into "); //insert into
SQL.Append(TableName);
SQL.Append(" ([");
int i;
for( i = 0 ;i< ziduan.Length ;i++) //字段
if (msg[i] != "_n_")
SQL.Append(ziduan[i]);
SQL.Append("],[");
SQL = SQL.Remove(SQL.Length -2,2);
SQL.Append(") values ('");
for( i = 0;i<ziduan.Length ;i++)
SQL.Append(msg[i]);
SQL.Append("','");
SQL.Append(") select scope_identity() as a1");
string re = RunSqlGetID(SQL.ToString());
SQL.Length = 1;
if (re == null)
return "-1";
else
return re;
/// 修改記錄。傳入表名,字段數組,值數組
/// <param name="TableName">要修改記錄的表的名稱</param>
/// <param name="tiaojian">條件 ,加在where 後面的語句</param>
public bool UpdateData( string TableName ,string[] ziduan ,string[] msg ,string tiaojian)
SQL.Append("update "); //update
SQL.Append(" set ");
for (i = 0 ;i< ziduan.Length ;i++)
SQL.Append("[");
SQL.Append(ziduan[i]); //update
SQL.Append("]='");
SQL.Append("',");
SQL = SQL.Remove(SQL.Length-1,1); //去掉最後一個 ","
SQL.Append(" where ");
SQL.Append(tiaojian);
RunSql(SQL.ToString());
思路呢就是 組合成 insert into 的SQL語句,insert 需要什麼資訊呢?表名,字段名和字段對應的值。
我把這三個資訊用一個字元串和兩個數組傳遞進來,然後用兩個for 循環來組合。最後通過 ADO.net 送出給資料庫執行。
這樣99%以上的表的添加都可以用這個函數了,是不是可以達到以下幾個目标呢?
1、抽象。資料通路層利用一個函數就可以應對多個表的添加資料的功能。
2、代碼重用。好多地方都可以調用。
3、零耦合。業務層和資料層的真正零偶合。我覺得修改上一層的代碼的時候不用修改下一層的代碼,這就是零耦合吧;同樣修改下一層的代碼的時候不用修改上一層的代碼,也就是零耦合吧。而這裡就是這樣。
隻要你把資料放在資料庫裡(有表有字段的那種),而且這種資料庫支援“insert into ”這樣的标準SQL語句,那麼就不需要修改資料通路層!上一層也不用修改。
我覺得這個不能叫做面向對象吧,頂多是利用了“封裝”的特性。應該是面向過程的思路。但是應該說使用了 抽象。
優點:
1、再多的表也不用一遍一遍的寫組合SQL語句的代碼了。(前提是使用SQL語句)。
2、SQL語句完全放在資料通路層裡,上一層隻出現表名和字段名。
3、代碼量少!資料通路層隻需要一個函數(還是公用的),也不用實體類來傳遞資料了,也就少了編寫實體類和指派取值的代碼。
4、便于修改,增加字段的話,隻需要修改一下數組的大小,加兩行代碼就可以了。(當然UI裡還要加一個控件)。其他的地方,資料通路層了根本就不用修改,根本就沒有實體類,是以也就不用修改了。
5、添加、修改一個頁面完成。
缺點:
1、雖然邏輯層裡沒有出現SQL語句,但是依然出現了表名和字段名。相信大家對這一點很是不滿意吧。這個确實是一個缺點,但是并不足以否掉這種添加資料的方案。
2、沒有使用實體類傳遞資料(也沒有用Hashtable),而使用了數組。我覺得數組很靈活,也很基本,絕大多數語言都是支援的,這裡使用數組就足夠用了吧。
3、不OO。不知道這個算不算缺點,難道不OO就不能寫成程式了嗎?