1. 程式人生 > >C#回顧學習筆記四十:三層架構

C#回顧學習筆記四十:三層架構

1)三層框架是什麼?
按照書籍和部落格文章裡千篇一律的解釋就是:UI層、BLL層、DAL層。這樣的解釋通常無法讓人一時半會理解含義,總而言之在這裡先大概說明一下三層架構。正如其他文章提到的一樣,三層架構就是:表示層(與使用者直接進行互動)、業務邏輯層(在表示層和資料訪問層中間聯絡兩者的重要角色)、資料訪問層(與資料庫實打實的做交易,存取資料之類的)。所謂UI層實際上就是跟使用者打交道的那一層,比如控制檯應用程式、asp.net web應用程式、窗體應用程式,這些專案檔案都是用於跟使用者打交道的,所以就是UI層。

2)為什麼使用三層框架?
假設有一個窗體應用程式,比如新建了一個winform窗體應用。使用者單擊註冊按鈕後,將各種資料提交到後臺,此時你可以直接呼叫ExecuteNonQuery()方法並將使用者資料寫入資料庫。而三層架構的特點就是將使用者註冊的業務提交給了BLL層,再由BLL層提交給DAL層處理問題,這樣使用者的註冊業務就經過了好幾步才完成。為什麼要這麼麻煩?
有這麼一家飯館,由於剛起步所以只有廚師來管理整個飯館。他們兼顧給客人點菜、做菜、上桌的事情。日子漸漸過去,飯館規模將要擴充,這時候考慮僱傭服務員來為客人點單並將選單交給廚房處理。增加服務員後,客人不會直接與廚師說要什麼菜,因為客人變多意味著廚師變忙,而廚師變忙亂或許還會影響正在做的菜。所以客人就可以將選單交給服務員,而服務員又將選單交給廚房做菜,等廚房菜做好之後,服務員又將菜交給相應的客人。這樣做的好處在於:①讓複雜的工作變得有秩序。②其中任意一個環節出了問題,能夠更加迅速發現並快速維護。③分工合作使得每個人的工作量變得固定,更加人性化。
程式碼的分層能讓不同職員負責不同的層,通過分層能將業務細節分解,將不同功能的程式碼分散出來。這樣做更加有利於系統的開發,業務越大可能分的層也越多,都是為了讓系統便於維護和擴充套件。從另一方面講,分工合作的優勢就是讓適合的人做自己擅長的事情,使得平均勞動時間變短,讓效率變高。軟體的分層實際上就是實現“高內聚、低耦合”。
就以上面的【使用者單擊註冊按鈕】的例子來講,所有的包括業務邏輯、資料庫操作用到的方法甚至sql語句都存在一個Form1類裡面,這就必須使得做這個專案的人不但要會業務邏輯,還要懂資料庫操作。如果分為三層來操作,那麼UI層的編碼人員不需要懂sql語句,它只要知道呼叫一個別人寫好的方法就可以獲得自己需要的東西就可以了,這樣結構更加清晰,分工更明確。

3)如何使用三層架構?
以一個從資料庫查詢表並返回到窗體上的例子來解釋,只要清楚了這個查詢怎麼實現,其他的增刪改查也用同樣的套路完成。
第1步,本次實驗使用SQLserver。先設計好資料庫,新建表Student並新增一些記錄,表字段如下:


第2步,新建Model類庫檔案,雖然Models不在三層架構的概念說明裡(UL、BLL、DAL),但卻是開發必要的。Models提供實體類,貫穿整個專案,三層都需要用到它。在解決方案上右鍵-新增-新建專案-類庫,名字叫Student.Model。在類庫檔案下新建實體類,名字是Students.cs,為了節省篇幅這裡使用自動屬性,程式碼如下:
public class Students  
    {  
        public string Address { get; set; }  
        public int? Weight { get; set; }  
        public int? Tall { get; set; }  
        public string Gender { get; set; }  
        public string Birthday { get; set; }  
        public string Name { get; set; }  
        public int Id { get; set; }  
    }
第3步,按照新增Model類庫檔案的做法新增一個DAL類庫檔案,名字叫Student.DAL。因為上面提到過實體類貫穿三層,因此DAL層要新增對Model的引用,在。在類庫檔案下新建SQLHelper類用於實現資料庫操作,新建StudentDAL類用於接收來自BLL層的增刪改查處理。
第4步,新建類庫檔案(DAL層),取名叫Student.DAL,上面提到過實體類貫穿整個專案,因此DAL層要新增對Student.Model的引用。在“引用”上右鍵-新增引用-解決方案,找到Student.Model並勾選確定。然後在DAL類庫檔案下新建StudentDAL類和SqlHelper類。StudentDAL類主要提供業務處理需要用到的方法,比如查詢表,比如根據Id查詢使用者,比如刪除使用者等等……這裡只提供查詢表的方法。SqlHelper類主要是對資料庫實際進行查詢操作需要用到的方法。詳細程式碼如下
StudentDAL類程式碼(注意先using Student.Model;):
public class StudentDAL
    {
        public DataTable Select()
        {
            DataTable table = SqlHelper.ExecuteQuery("select * from Student");
            return table;
        }
    }
SqlHelper類程式碼:
class SqlHelper
    {
        private static readonly string connStr = ConfigurationManager.ConnectionStrings["testConn"].ConnectionString;
        
        public static SqlConnection createConnection()
        {
            SqlConnection conn = new SqlConnection(connStr);
            conn.Open();
            return conn;
        }


        //執行查詢:select返回多行多列
        public static DataTable ExecuteQuery(SqlConnection conn, string sql, params SqlParameter[] parameters)
        {
            DataTable table = new DataTable();
            using (SqlCommand cmd = conn.CreateCommand())
            {
                cmd.CommandText = sql;
                cmd.Parameters.AddRange(parameters);
                using (SqlDataReader reader = cmd.ExecuteReader())
                {
                    table.Load(reader);
                }
            }
            return table;//返回載入的表
        }


        public static DataTable ExecuteQuery(string sql, params SqlParameter[] parameters)
        {
            using (SqlConnection conn = createConnection())
            {
                return ExecuteQuery(conn, sql, parameters);
            }
        }
    }
注意:SqlHelper類中的ExecuteQuery()方法構成過載,主要是方便DAL層呼叫這個方法時少傳入一個SQLConnection物件的引數,這些都在SqlHelper類內部進行處理並自動呼叫真正實現查詢操作的方法來處理資料了。
第5步,新建類庫檔案(BLL層),取名叫Student.BLL。BLL層需要將任務提交給DAL層處理,並且上面提到過實體類貫穿整個專案,因此BLL層要新增對DAL層和Model的引用,參照上面的方式,新增對Student.DAL和Student.Model的引用。在BLL類庫檔案下新建StudentBLL類,詳細程式碼如下
StudentBLL類的程式碼(注意先using Student.Model;和using Student.DAL;):
public class StudentBLL
    {
        private StudentDAL sDal = new StudentDAL();
        public DataTable Select()
        {
            return sDal.Select();
        }
    }
第6步,新建winform窗體應用,這表示UI層。假設名字叫Student.Winform。先using Student.BLL;,然後在窗體上新增一個DataGridView控制元件,圖片如下:


第7步,在Student.Winform窗體應用程式下的App.config裡配置資料庫連線字串,在 <configuration>標籤裡配置如下程式碼:
<connectionStrings>
    <add name="testConn" connectionString="server=.;database=CRUDTest;uid=sa;pwd=145217"/>
  </connectionStrings>
其中name屬性的值與DAL層下SqlHelper類中的ConnectionStrings["testConn"]保持一致。server表示資料庫地址,小數點代表本地地址。database表示資料庫名字。uid是資料庫登入使用者名稱。pwd是資料庫登入密碼。
接著新增窗體應用程式的程式碼,主要新增一個Load事件,詳細程式碼如下:
public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            StudentBLL bll = new StudentBLL();
            DataTable table = bll.Select();
            dataGridView1.DataSource = table;
        }
    }
第8步,將Student.Winform設為啟動項並執行程式,如果能觀察到執行結果則說明沒有問題。

4)對上面程式碼執行過程做解釋
1.執行窗體程式會自動執行Load事件的程式碼,先宣告StudentBLL類的物件,然後就可以呼叫BLL層的Select()方法。
2.BLL層會先宣告StudentDAL類的物件,然後繼續呼叫DAL層的Select()方法。BLL層並不實現查詢操作,只是將這個任務交給DAL層處理了。
3.DAL層的Select()方法會呼叫SqlHelper類的ExecuteQuery()方法。在SqlHelper類內部又會先呼叫寫好的獲得一個SQLConnection物件的方法,並呼叫構成過載的另一個ExecuteQuery()方法。
4.構成過載的這個ExecuteQuery()是具體實現資料庫操作的方法,查詢到表結果後逐步返回到DAL層的Select()方法裡。
5.DAL層的Select()方法已經獲得呼叫ExecuteQuery()方法返回的表結果,並將這個表結果返回到呼叫它的BLL層裡。BLL層又將表結果返回到呼叫BLL層的UI層裡。
6.UI層裡執行dataGridView1.DataSource = table;用於將表資料填充到控制元件上顯示。至此完成表資料查詢操作。

5)三層架構的概念補充
1.參照上面對三層架構的講解與練習,實際上可以用一張圖片來解釋上面的執行過程。理解三層架構的含義後,甚至用這張圖解釋上面提到的餐館例子也可以。UI層相當於客戶,BLL層相當於服務員,而DAL層相當於廚師。

2.有程式碼生成器可以自動生成三層架構的專案,甚至連資料庫增刪改查方法都提供了。比如“動軟程式碼生成器”,當然不要太依賴軟體的“自動”。

3.或許在做了上面三層架構的練習後,會覺得BLL層的功能微乎其微。實際上在BLL層可以進行邏輯判斷,這也正是BLL層的好處所在。比如BLL層拿到DAL層返回過來的資料後,可以進行相應的if判斷,確保資料滿足業務需求,再返回給UI層。

相關推薦

C#回顧學習筆記架構

1)三層框架是什麼? 按照書籍和部落格文章裡千篇一律的解釋就是:UI層、BLL層、DAL層。這樣的解釋通常無法讓人一時半會理解含義,總而言之在這裡先大概說明一下三層架構。正如其他文章提到的一樣,三層架構就是:表示層(與使用者直接進行互動)、業務邏輯層(在表示層和資料訪問層中

C#回顧學習筆記程式設計師必備技能,除錯

編寫程式碼免不了會出現各種錯誤,在執行時會發現不是拋異常就是執行結果跟預期結果有差異。這時候就需要思考如何去解決這個問題。 首先應該清楚一點:程式碼都是從頭開始,一句一句往下執行。程式設計師在發現程式碼有問題時,首先要想到的就是,這個錯誤可能發生在第幾句程式碼。有經驗的程式

opencv學習筆記背景消除建模

在很多情況下,我們需要從一段視訊中找到感興趣的目標,比如說當人進入已經打烊的超市時發出警報。為了達到這個目的,我們首先需要“學習”背景模型,然後將背景模型和當前影象進行比較,從而得到前景目標。 背景建模(Background Subtraction)     背景與前景都是

C#回顧學習筆記字典集合Dictionary

1.字典集合是什麼?字典集合本身像它名字那樣通俗易懂,也就是“根據**查找出**”。字典集合的格式通常是: Dictionary<鍵的型別,值的型別> dic = new Dictionary<鍵的型別,值的型別>(); Dictionary字典集合

C#回顧學習筆記事務

1)事務是什麼? 事務是保證多個操作全部成功時才認為是一次有效操作,當有一個操作失敗時就會認為全部操作無效,並且回到執行操作之前的狀態只有資料改變時(增加、修改、刪除)時才會引發事務,查詢不會引發事務。如果在寫入一個記錄時出現失敗,則事務會讓其他已經寫入的資料回滾,讓資料

【Unity 3D】學習筆記粒子特效

空間 獲得 material package 一個 log 創建 spa mpi 粒子特效 粒子特效的原理是將若幹粒子無規則的組合在一起。來模擬火焰,爆炸。水滴,霧氣等效果。要使用粒子特效首先要創建,在hierarchy視圖中點擊create——particle s

opencv學習筆記稠密光流跟蹤

利用Gunnar Farneback演算法計算全域性性的稠密光流演算法(即影象上所有畫素點的光流都計算出來),由於要計算影象上所有點的光流,故計算耗時,速度慢  稠密光流需要使用某種插值方法在比較容易跟蹤的畫素之間進行插值以解決那些運動不明確的畫素 calcOpticalF

opencv學習筆記移動物件統計

步驟: 利用背景消除法找到移動的物體; 預處理:進行中值濾波消除椒鹽噪聲,然後二值化再開操作; 尋找輪廓; 畫出輪廓最小矩形並統計。 #include<opencv2\opencv.hpp> using namespace cv; using namesp

opencv學習筆記基於距離變換和區域性自適應閾值的物件計數

案例背景:統計下圖中玉米粒的個數 方案思路:先灰度化,再二值化(基於THRESH_TRIANGLE,圖中直方圖有明顯的雙峰值),腐蝕去掉一些小雜點,距離變換,再自適應區域性閾值,膨脹連成連通域,尋找輪廓進行計數。 距離變換於1966年被學者首次提出,目前已被廣泛應

【Unity 3D】學習筆記路徑渲染

路徑渲染 路徑渲染屬於特效渲染元件,用於跟隨運動中的遊戲物件。首先在hierarchy檢視中,建立一個球體。然後在選單導航欄中選擇component——effects——trial renderer即可將路徑渲染元件新增至該球體物件中。 cast shadows:顯示陰

Androin學習筆記Java android Socket通訊檢測(server)連線是否斷開

Pre 在利用socket寫通訊程式的時候,想檢測伺服器是否還活著。 從網上找了很多資料,都沒有自己合適的,最後自己想了個辦法,不過也相當於截取了心跳檢測的一部分。 這裡檢測的是遠端server的連線,而不是本地是否連線成功。首先想到socket類的方法isClosed()、isConnected

【Unity 3D】學習筆記輸入與控制——鍵盤事件

在遊戲中,玩家控制主角移動,按鍵攻擊,選擇行走。都需要在程式中監聽玩家的輸入。unity為開發者提供了input庫,來支援鍵盤事件,滑鼠事件以及觸控事件。本文主要回顧鍵盤事件,以後會逐文複習滑鼠以及觸控事件。 鍵盤事件 一般的PC鍵盤有104個不同的按鍵,在程式中通過監

opencv學習筆記影象拼接

簡要: 影象拼接在實際的應用場景很廣,舉一個例子,用你的手機對某一場景拍照,但是沒有辦法一次將所有你要拍的景物全部拍下來,所以你對該場景從左往右依次拍了好幾張圖,來把你要拍的所有景物記錄下來,影象拼接就是要將這些影象拼接成一個完整的大圖。 核心: 特徵點檢測 特徵點匹配

opencv學習筆記十三CamShift目標跟蹤

CamShift演算法,全稱是 Continuously AdaptiveMeanShift,顧名思義,它是對Mean Shift 演算法的改進,能夠自動調節搜尋視窗大小來適應目標的大小,可以跟蹤視訊中尺寸變化的目標。基本思想是以視訊影象中運動物體的顏色資訊作為特徵,對輸入影

opencv學習筆記透視變換綜合例項

案例背景:對下圖發生畸變的影象進行校正  方案思路:灰度二值化分割,閉操作,尋找輪廓,霍夫直線檢測,直線排序,直線方程,直線交點,透視矩陣,透視變換。 #include<opencv2\opencv.hpp> using namespace cv; usi

C++ primer學習筆記——第章 泛型演算法

標準庫容器定義的操作集合驚人的小。標準庫並未給每個容器新增大量功能,而是提供了一組演算法,這些演算法中的大多數都獨立於任何特定的容器。這些演算法是通用的(generic,或稱泛型的):它們可以用於不同型別的容器和不同型別的元素 一、概述 大多數演算法都定義在標頭檔案alg

C語言學習筆記

break本質是受限的goto語句,跳轉的位置限定為緊接著迴圈語句後的第一條語句。 exit(0);終止整個程式的執行強制返回作業系統,當其引數為0時,表示程式出現某種錯誤後退出 continue與break的區別: break:退出一層迴圈或switch,轉到

C語言學習筆記二)

函式原型與函式定義的區別: 函式定義 函式原型 指函式功能的確立 對函式名、返回值型別、形參型別進行宣告 有函式體

Redis學習筆記Redis用作訊息佇列

package org.yamikaze.redis.messsage.subscribe; import org.yamikaze.redis.test.MyJedisFactory; import redis.clients.jedis.Jedis; import redis.clients.jedis

【Unity 3D】學習筆記十三布料

布料 布料是特殊的元件,它可以變化成任意形狀,比如說:隨風飄的旗子,窗簾等 建立布料的方法有兩種:建立布料物件,在遊戲物件中添加布料元件。前者通過hierarchy檢視中選擇create——cloth即可,建立後,系統會自動將互動布料元件(interactive clot