我用了十分鐘看懂了三層架構!卻用半年時間去磨合它們。
一、三層概念
(一) 三層基本概念
1、 UI層:用戶表示層,主要是指與用戶交互的界面。用於接收用戶輸入的數據和將用戶需要的數據以不同的方式展示給用戶。
2、 BLL層:業務邏輯層,UI層和DAL層之間的橋樑,主要處理與業務邏輯相關的操作,像一個人的大腦,所有的邏輯判斷、業務操作都在此層處理,包含:驗證、計算、業務規則等等。如:銀行轉帳:判斷本帳戶和對方帳號是否存在,金額是否充足,轉帳。這些都在業務層處理。
3、DAL層:數據訪問層,很單純,只負責操作數據庫。主要實現對數據的增、刪、改、查。將存儲在數據庫中的數據提交給業務層,同時將業務層處理的數據保存到數據庫。(當然這些操作都是基於UI層的。用戶的需求反映給界面(UI),UI反映給BLL,BLL反映給DAL,DAL進行數據的操作,操作後再一一返回,直到將用戶所需數據反饋給用戶)
4、MODEL層:實體層,實體類不屬於三層中的任何一層,但在三層之間傳遞數據。通過傳遞一個參數就可以查詢出相關的數據的所有結果,並把這個結果都存到實體類對象中,以後需要用這些查詢到的結果時,只需要調用實體類對象就行了。這樣就不需要把每列都當一個參數傳進來了。如果列非常多,那麼會相當多的參數,會很複雜。
(1)實現面向對象思想中的"封裝";
(2)貫穿於三層,在三層之間傳遞數據;
(注:確切的說實體層貫穿於三層之間,來連接三層)
(3)對於初學者來說,可以這樣理解:每張數據表對應一個實體,爲每個需要操作的表在實體層中創建一個類,每個數據表中的字段對應類中的屬性。
(注:當然,事實上不是這樣。爲什麼?
1>,可能我們需要的實體在數據表對應的實體中並不存在;
2>,我們完全可以將所有數據表中的所有字段都放在一個實體裏)
(4)每一層(UI—>BLL—>DAL)之間的數據傳遞(單向)是靠變量或實體作爲參數來傳遞的,這樣就構造了三層之間的聯繫,完成了功能的實現。
但是對於大量的數據來說,用變量做參數有些複雜,因爲參數量太多,容易搞混。比如:我要把員工信息傳遞到下層,信息包括:員工號、姓名、年齡、性別、工資....用變量做參數的話,那麼我們的方法中的參數就會很多,極有可能在使用時,將參數匹配搞混。這時候,如果用實體做參數,就會很方便,不用考慮參數匹配的問題,用到實體中哪個屬性拿來直接用就可以,很方便。這樣做也提高了效率。
5、各層之間的聯繫:
(1)各層引用
UI層要引用BLL層和MODEL層。
BLL層要引用DAL層和MODEL層。
DAL層要引用MODEL層。
(2)參數傳遞
每一層(UI—>BLL—>DAL)之間的數據傳遞(單向)是靠變量或實體作爲參數來傳遞的,這樣就構造了三層之間的聯繫,完成了功能的實現。UI層把參數傳給BLL層,BLL層把參數傳給DAL層,是單向的,不能反傳,即BLL層不能把參數傳給UI層。
二、編寫三層的一般步驟
(一) 建立三層項目前提及注意示項
(1)在解決方案上右鍵分別添加BLL,DAL,MODEL類庫,UI層根據不同的要求可能會建成WINFORM或webform項目。。
(2)UI層引用BLL和MODEL;BLL引用DAL和MODEL;DAL引用MODEL;MODEL無引用。
(3)注意點:
實體類(MODEL)中,爲每個實體(理解爲要操作的表,即一個表爲一個實體)建一個方法,以表名爲方法名,字段名爲表的各列的列名,併爲字段建立屬性。實體的作用是爲三層存儲或傳遞值。
在DAL層和BLL層中爲每個數據庫表建單獨建立一個類,所有對這個表的操作都通過這個類中的方法實現。
注意各類中的名字空間的引用。最好定義規範的名字空間。如:項目爲loready,那麼建立MODEL類庫時,就用loready.MODEL,DAL類庫時用loready.DAL,BLL類庫用loready.BLL。
(4)前提條件:
要有sqlhelper,且把sqlhelper放在DAL層中
如有p.confg要放在UI層中
(二) 建立三層的步驟及示例
1、確定需求
輸入用戶名和帳號後判斷是否登錄成功,如果成功則返回登錄成功提示;如果用戶名正確,密碼錯誤,反回密碼錯誤提示;如果沒有此用戶名,則返回沒有此用戶提示。
2、根據需求確定SQL語句以及要操作的數據庫表。
"select Uno,Uname,Upasswd,Uremark from username whereUname=@uname";
//根據用戶名可以查到相應用戶名的ID,用戶名,密碼,備註信息。
3、根據要操作的數據庫表寫實體類,併爲每個要操作的表建立一個單獨的實體類。
publicclassusernameMODEL //把每個表作爲一個實體類,並把每個字段作爲一個屬性聲明出來,以存儲與此表相關的查詢結果。
{
privatestringuno;
publicstringUno
{
get{ return uno; }
set{ uno = value; }
}
privatestringuname;
publicstringUname
{
get{ return uname; }
set{ uname = value; }
}
privatestringupasswd;
publicstringUpasswd
{
get{ return upasswd; }
set{ upasswd = value; }
}
privatestringuremark;
publicstringUremark
{
get{ return uremark; }
set{ uremark = value; }
}
4、編寫數據訪問層(DAL層)
編寫執行該sql語句的數據訪問層,一般爲每個數據庫表創建一個單獨的類,對這個表的所有數據訪問都在這個類中通過方法實現,不同的數據訪問創建不同的方法。
publicclassusernameDAL
{
//根據某個用戶名查出這個用戶名的所有信息,包含ID,用戶名,密碼等等。這些信息查出後可用在其他地方。
publicusernameMODEL getbyusername(stringlogin) //根據傳進來的用戶名查詢此用戶名的所有相關數據。並把這個用戶的所有數據都返回給實體類對象。
//實體類不屬於三層中的任何一層,但在三層之間傳遞數據。通過傳遞一個參數就可以查詢出相關的數據的所有結果,並把這個結果都存到實體類對象中,以後需要用這些查詢到的結果時,只需要調用實體類對象就行了。這樣就不需要把每列都當一個參數傳進來了。如果列非常多,那麼會相當多的參數,會很複雜。
{
usernameMODEL model=null; //建一個實體類的對象,並賦空值。因爲如果不賦空值,會默認賦值的。
string s = "selectUno,Uname,Upasswd,Uremark from username where Uname=@uname"; //寫出sql語句
SqlParameter par = newSqlParameter("@uname",login); //增加參數
SqlDataReader read = sqlhelper.ExecuteReader(s, CommandType.Text,par); //因爲查出來的是整條語句或多條語句,所以要調用sqlhelp中的excutereader方法。
using(read)
{
if (read.HasRows)
//如果查詢的結果中有行存在(有數據存在),則說明根據相應的用戶名找到了結果。hasrows判斷斷是否包含一行或多行
{
if (read.Read()) //如果讀取到數據則按序號把值賦給實體中的相應字段。
{
model = newusernameMODEL(); //建立一個實體類對象
model.Uno =read.GetString(0); //第0序號代表第一列,即NO列。
model.Uname =read.GetString(1); //第1序號代表第二列。
model.Upasswd =read.GetString(2);
model.Uremark=read.GetString(3);
}
}
}
returnmodel; //把結果返回給實體類對象。
}
5、編寫BLL層
爲每個數據庫表在業務邏輯層單獨創建一個類,每個表的所有業務邏輯都在此類中通過方法實現。
publicclassusernameBLL
{
stringno = "no";
stringok = "ok";
stringerro = "erro"; //定義三種可能反回的字符串。可以先建一個類,在類裏定義一個枚舉方法,把這三個值放進去,再調用枚舉即可。
publicstringlogin(string login, stringpassword,outstringid,outstringusername) //把用戶輸入的用戶名和密碼傳進來。
//如果數據庫表中存儲的密碼是MD5運算後的字符串,密碼字符串就要先進行MD5運算。
//out參數是指我們還要返回一些其他的值,如我們要返回這個用戶的ID號和用戶名。
{
id = string.Empty;
//先給這兩個字符串賦空值。如果用戶名正確就會返回相應的用戶名和ID,如沒有此用戶名則會返回這兩個空值。
username = string.Empty;
usernameMODEL model1 = newusernameMODEL(); //創建一個實體對象,以調用數據或存儲數據。
usernameDAL udal = newusernameDAL(); //創建一個DAL對象,以調用DAL層中的方法。
model1 = udal.getbyusername(login); //調用DAL對象的方法,並把返回的值存到實體類對象中。
if(model1 != null) //如果實體中不爲空,則說明找到了相應的用戶的數據。
{
if (login == model1.Uname &&password == model1.Upasswd)
//如果用戶名和密碼都正確,就返回OK字符串。
{
id = model1.Uno; //如果用戶名和密碼正確,就返回用戶名和ID的值。
username = model1.Uname;
return ok;
}
else //如果找到了相關用戶,卻密碼不正確就返回erro字符串。
{
id = model1.Uno;
username = model1.Uname;
return erro;
}
}
else 實體類對象中爲空說明沒有這個用戶名,則近回No字符串的值,用戶名不存在。
{
returnno;
}
6、編寫UI層
privatevoidbutton1_Click(object sender, EventArgse)
{
usernameBLL bll = newusernameBLL(); //建立bll層對象
stringlogin1=user.Text.Trim(); //獲取用戶輸入的用戶名
stringpassword1=pwd.Text; //獲取用戶輸入的密碼
stringid; //out參數id的聲明
stringusername; //out參數username聲明
stringstr = bll.login(login1, password1,out id,outusername);
//調用blld層的login方法,並把用戶輸入的用戶名和密碼傳進去。如有Out參數一定要加上。
switch(str) //通過返回的字符串來判斷是否登錄成功
{
case"no": //如果返回的字符串是no,則表示用戶名不成在。no是調用bll層中方法的返回
MessageBox.Show("用戶名和密碼不存在");
break;
case"ok": //如果是Ok則說明登錄成功
MessageBox.Show("登錄成功");
idshow.Text = id; //把兩個lable的值改成Out 參數返回的值,一個是返回的ID號,一個返回的是用戶名。
usernameshow.Text = username;
break;
case"erro": //如果返回值是erro則說明用戶名正確,但是密碼錯誤。
MessageBox.Show("密碼錯誤");
idshow.Text = id; //也顯示用戶ID和用戶名。
usernameshow.Text = username;
break;
default:
MessageBox.Show("未知錯誤");
break;
}