“七层架构”-----实践篇-登录小实例
上一篇博客小編簡單介紹了一下近期在軟件開發過程中由三層架構演變而來的“七層架構”基本理論點。理論知識與產生結果之間還夾雜著一個重要的點---實踐。用實踐來檢驗理論知識,豐富知識內涵、加深對其理解。接下來小編用一個簡單的小實例來為您分享由三層架構演變而來的“七層架構”。
【1】首先,搭建七層結構的框架;
搭建結構可以仿照小編前面的博文<三層實踐>來完成哦。
【2】其次,設置好層與層之間的引用關系;
具體的引用關系,請參看<上篇博客>。
【3】再次,添加需要的類;
添加方式與<三層實踐>相同哦。
【4】最后,設計UI并進行編碼。
這一步驟也與<三層實踐>是大抵相同的,這里不再重復贅述。
接下來主要以員工登錄功能需求為例,為您分享“七層架構”是如何實現這一功能需求的。探索層與層之間是如何取得聯系的、參數是如何傳遞的、每一層有著哪些更為具體的作用。
?? 首先,在D層下新建的類中封裝需要的SqlHelper方法,用于創建數據庫的連接。
public class SQLHelper{//聲明對象private SqlConnection conn = null; //SqlConnection 打開連接private SqlCommand cmd = null; //SqlCommand對象允許你指定在數據庫上執行的操作的類型。比如,你能夠對數據庫中的行數據執行select,insert,modify以及delete命令。//SqlCommand對象能被用來支持斷開連接數據管理的情況,可以只單獨使用SqlCommand對象。也可以與SqlDataAdapter一起實現斷開數據連接,實現操作數據庫的應用程序private SqlDataReader sdr = null; //讀取只進的行流的方式//構造方法public SQLHelper(){string connStr = ConfigurationManager.AppSettings["connStr"]; //定義連接數據庫字符串參數。connStr 是配置文件里連接數據庫的關鍵字,引用這一句可以連接數據庫conn = new SqlConnection(connStr); //實例化一個連接,將連接數據庫的字符串參數傳遞給連接對象}// 將數據庫連接打開private SqlConnection GetConn(){if (conn.State == ConnectionState.Closed) //如果連接狀態為關閉 {conn.Open(); //則打開}return conn;}/// <summary>/// 執行 不帶參數 的 增刪改SQL語句 或者 存儲過程/// </summary>/// <param name="cmdText">增刪改SQL</param>/// <param name="ct">命令類型</param>/// <returns>返回受影響的行數</returns>public int ExecuteNonQuery(string cmdText, CommandType ct)//增刪改 SQL 命令類型{int res;try{cmd = new SqlCommand(cmdText, GetConn()); //打開連接cmd.CommandType = ct;res = cmd.ExecuteNonQuery();}catch (Exception ex){throw ex;}finally{if (conn.State == ConnectionState.Open){conn.Close(); //關閉數據庫 與上面相對應}}return res;}/// <summary>/// 執行 帶參數的的 增刪改SQL語句 或者 存儲過程/// </summary>/// <param name="cmdText">增刪改SQL</param>/// <param name="paras">要查詢的參數</param>/// <param name="ct">命令類型</param>/// <returns>返回受影響的行數</returns>public int ExecuteNonQuery(string cmdText, SqlParameter[] paras, CommandType ct){int res;using (cmd = new SqlCommand(cmdText, GetConn())) //使用using,在三層實踐篇中有介紹哦 {cmd.CommandType = ct;cmd.Parameters.AddRange(paras);res = cmd.ExecuteNonQuery();}return res;}/// <summary>/// 執行 不帶參數的 查詢SQL語句 或 存儲過程/// </summary>/// <param name="cmdText">查詢SQL語句或存儲過程</param>/// <param name="ct">命令類型</param>/// <returns></returns>public DataTable ExecuteQuery(string cmdText, CommandType ct){DataTable dt = new DataTable();cmd = new SqlCommand(cmdText, GetConn());cmd.CommandType = ct;using (sdr = cmd.ExecuteReader(CommandBehavior.CloseConnection)){dt.Load(sdr);}return dt;}/// <summary>/// 執行 帶參數的 查詢SQL語句 或 存儲過程/// </summary>/// <param name="cmdText">查詢SQL語句或存儲過程</param>/// <param name="paras">參數集合</param>/// <param name="ct">命令類型</param>/// <returns></returns>public DataTable ExecuteQuery(string cmdText, SqlParameter[] paras, CommandType ct){DataTable dt = new DataTable();cmd = new SqlCommand(cmdText, GetConn());cmd.CommandType = ct;cmd.Parameters.AddRange(paras);using (sdr = cmd.ExecuteReader(CommandBehavior.CloseConnection)){dt.Load(sdr);}return dt;}#endregion}? ? ? ? ?? 在封裝的SqlHelper方法中,為什么要把對數據庫的 “查”操作與“增刪改”操作的方法分開來寫呢?
? ? ? ? 最本質的原因是,Sql Server執行Select語句與Insert、Delete、Update語句時,返回的結果是不一樣的。前者返回的結果是一個臨時表,而后者返回的結果是受到影響的行數。
??其次,在每一層中編寫實現功能的方法。
[1] 實體層:定義字段、屬性,用于實現功能過程中的傳參。
namespace Entity {public enum StaffLoginState//采用枚舉的方法,列登錄時可能出現的情況{OK,//成功NoError,//失敗}public class StaffInfo //StaffInfo類型所需要的字段。字段名稱最好與數據庫表中字段保持一致,方便區分。{private int adminid;public int adminID{get { return adminid; }set { adminid = value; }}private string adminname;public string adminName{get { return adminname; }set { adminname = value; }}private string password;public string Password{get { return password; }set { password = value; }}private string head;//級別public string Head{get { return head; }set { head = value; }}private string isdelete;//是否已注銷public string Isdelete{get { return isdelete; }set { isdelete = value; }}} }[2] 接口層:只是用來定義實現功能所需的方法名。這一層只有抽象的方法名,而沒有具體實現功能的方法體。
namespace IDAL {public partial interface StaffIDAL{StaffInfo SelectByID(int id);//定義一個類型為Staffinfo 名為SelectByID 帶參數的方法。這是根據ID查詢員工信息的方法,用于員工登錄等功能。} }?
[3] 數據訪問層:D層實現接口層中定義的方法。該層中的方法名 要與接口層中定義的方法名保持一致,且有具體實現功能的方法體。
namespace DAL {public partial class StaffDAL:StaffIDAL //實現接口的方法{#region 根據員工ID 獲取員工信息---用于員工登錄public StaffInfo SelectByID(int id){ StaffInfo staff = null ;//聲明一個用戶SQLHelper sqlHelper = new SQLHelper(); //實例化類 跳轉到SQLHelper方法string sql = @"select * from Staff_Info where adminID=@adminID and isdelete='否'";//構造Select查詢語句 匹配數據庫SqlParameter p = new SqlParameter("@adminID", id);//為語句構造參數DataTable dt = sqlHelper.ExecuteQuery(sql, p,CommandType.Text); //連接數據庫 執行查詢得到結果if (dt.Rows.Count >0) //判斷數據表里是否查到結果{staff = new StaffInfo()//若用戶存在{//獲取用戶信息adminID=Convert.ToInt32(dt.Rows[0][0].ToString()),Password=dt.Rows [0][2].ToString(),adminName=dt.Rows[0][1].ToString(),Head=dt.Rows[0][3].ToString(),};}return staff;//返回一個用戶對象}#endregion} }[4] 工廠層:設置配置文件信息,選取所需的數據庫;創建相應的接口對象來實現接口層中的方法。
? ? ? 在App.config中設置配置文件信息:
?
?
上面是從網上查到的配置文件信息的兩種方法,用其中的一組(appSettings 或 connectionStrings)就可以實現目前所需的功能。至于這兩種有什么更為具體的區別,目前小編還不是很清楚哦。
配置文件的方式不同,就會影響到SQLHelper中連接數據庫的方法。以上兩種配置文件的形式分別對應下面的SQLHelper中連接數據庫所需拼接的字符串:
在工廠層下的類中編寫代碼:
//需要添加的引用。 using System.Reflection; using System.Configuration;namespace Factory {public class StaffFactory{private string StrDB = ConfigurationManager.AppSettings["DB"]; //接收來自配置文件的數據public IDAL.StaffIDAL CreateUser(){string ClassName = StrDB + "." + "StaffDAL"; //這里的StaffDAL是D層的類名 return (IDAL.StaffIDAL)Assembly.Load(StrDB).CreateInstance(ClassName); //反射加工廠的應用}} }這里在添加引用時,需要先在工廠層的引用下 添加該程序集的引用。
?
工廠+反射的應用,是為在大型項目開發過程中方便更換數據庫。
[5] 業務邏輯層:進行邏輯判斷。
namespace BLL {public partial class StaffBLL{private StaffIDAL idal;StaffFactory fact = new StaffFactory();//實例化工廠對象public StaffBLL () //構造方法{idal = fact.CreateUser();}public StaffLoginState Login(int id,string pwd,out string head,out string adminame){//使用out傳值 需要給參數定義初值head = null;//定義head為空值adminame = null;//根據用戶名進行對象查詢StaffIDAL idal = fact.CreateUser();//調用工廠方法創建接口StaffInfo staff = idal.SelectByID(id);//接收D層的返回值if (staff ==null) //判斷是否存在用戶對象{//用戶不存在//用戶名或密碼錯誤return StaffLoginState.NoError;}else{//用戶存在 head = staff.Head;//返回用戶頭銜 及姓名adminame = staff.adminName;return StaffLoginState.OK; }}} }[6] 外觀層:解耦U層和B層,接收和傳遞參數。
namespace Facade {public class StaffFacade{StaffBLL staffBll = new StaffBLL();//實例化B層對象//用戶登錄public StaffLoginState Login(int id,string pwd,out string head,out string username){return staffBll.Login(id, pwd, out head, out username);//接收B層返回的值,并將其傳給U層}} }[7] 顯示層:與用戶直接關聯,接收用戶輸入的信息,并返回結果。
namespace UI {public partial class frmLoginStaff : Form{public frmLoginStaff(){InitializeComponent();}//聲明靜態變量 用于其他功能獲取登錄的用戶名 傳值// 登錄的方法private void Login(){if (txtUserID.Text.Trim ()==""||txtPWD .Text==""){MessageBox.Show("請將登錄信息填寫完整");return;//停止后面代碼的執行 返回到if之前}//聲明變量,接收返回的值string head;string adminame;//獲取用戶輸入的信息int ID = Convert.ToInt32(txtUserID.Text.Trim());string pwd = txtUserID.Text;StaffFacade staffFacade = new StaffFacade();StaffLoginState state = staffFacade.Login(ID, pwd, out head, out adminame);switch (state){//登錄成功case StaffLoginState.OK:staff.Head = head;staff.Adminid = ID;staff.adminName = adminame;this.Hide();MessageBox.Show("登錄成功");frmMainStaff main = new frmMainStaff();main.Tag = head;//判斷用戶級別,用于給主窗體傳值 main.Show(); break;//登錄失敗case StaffLoginState.NoError:MessageBox.Show("用戶不存在或輸入信息錯誤");txtPWD.Text = "";txtUserID.Text = "";break;default:break;}}} }到此為止,基本的編碼工作就完成啦。點擊登錄,迫切的希望看到“登錄成功”彈出框,然后并沒有。經過不斷調試,出現的最大問題就是依賴項,找不到文件。解決這問題可是花費了小編不少精力呢。
? ? ?? ??按照三層架構的基本理論,UI層只需引用與其最直接關聯的BLL層即可,而沒有引用DAL層。之前在用三層架構做項目時就出現了缺少依賴項,找不到文件的問題。當時解決辦法就是簡單的上網查查,通過復制DAL層文件到UI層Debug文件夾下來解決的。這次用七層做項目又遇到了這個問題,這一次在解決這個問題的時候真是花費了不少時間呢。這次的解決辦法沒有采用復制文件的方式,而是在UI層中增加對DAL層引用。
? ? ?? ??為什么UI層還需要引用與其間接相關的DAL層呢?
? ? ? 【在此之前也嘗試過BLL層、Facade層等凡是沒有引用DAL層的都引用了一遍,但又會出現新Bug】
? ? ? 最本質的原因就是DAL層中的方法才是真正實現業務功能的最底層、最元(元:不可再分)始的方法,它包含與數據庫直接關聯的信息。按照理論點的話,只有直接依賴的層需要引用,沒有間接引用。但實際上U層需要的文件應該是產生在U層debug下,在沒有添加D層的引用時,生成的U層文件中沒有D層真正實現功能的文件。雖然U層最直接依賴的是Facade層,但D層dll文件包含真正實現功能的方法。換句話說,Facade層中封裝的方法 是對與其直接關聯的層中方法的封裝,是可拆分的方法;每層中的方法拆分后,最小單位的方法便是DAL層中的方法,它是不可再分的封裝的方法。所以實際做項目過程中還是間接引用了D層,在生成解決方案時,D層中的文件也同時放在了U層中。這樣程序在運行過程中就可以直接從UI層的Debug文件路徑下找到DAL層中實現功能需求的.dll文件了。或者復制D層文件、更改輸出路徑到U層下。但個人還是傾向將U層添加D層引用。
到此為止,小編為您分享的“七層架構”就要告一段落啦。實踐出真知,實踐能讓我們對理論知識有進一步的認識與理解,豐富知識的內涵。
感謝您的瀏覽,希望能對您有所幫助哦!
?
總結
以上是生活随笔為你收集整理的“七层架构”-----实践篇-登录小实例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: “七层架构”---理论篇
- 下一篇: 【HTTP】get 和 post 两种基