一、簡介
在上一篇將MVC的時候,有很有朋友對簡單三層的概念不是很熟悉,因此,今天進行簡單三層的一個簡單介紹,同時為理解MVC中的Model做知識累計。
傳統的三層主要指的是UI層,BLL層,DAL層:
- UI層:與用戶進行交互的前台層,主要負責展示數據給前台,以及接受數據到后台。
- BLL層:可以叫它業務層,主要負責處理業務邏輯,比如說數據的校驗等等操作。
- DAL層:很重要的數據訪問層,主要負責和數據庫進行交互,完成數據的讀取以及寫入等操作。
這里沒有提到Models,可能會有人感到驚奇,Models是三層的概念,但不是三層的層概念,Models是一個存放實體類型的層。
上面是理論概念,下面再講下他們之間的調用關系,然后我們就上一些例子來促進理解。。。。。。
對上圖進行簡單的介紹:這樣的分層,主要目的是將業務邏輯和數據訪問進行一個分離。如果不進行這樣的分層,混合在一起,當你的數據庫進行一下調整,你就需要將所有進行數據訪問的地方都修改,那就太痛苦了,你會有瘋了的沖動,嘿嘿。但是,如果這樣分層后,你只需要修改DAL層里面的方法,BLL不需要修改,是不是感覺到一點分層的意義了。
二、代碼層面理解
我們先理解下Model實體,這個可是貫穿三層的東東,下面來看下一張數據表:
可以對應的實體類,來看看,我們在構造實體類的時候,要注意類型對應:

//============================================================ //author:zhujinghui //============================================================ using System; using System.Collections.Generic; using System.Text; namespace ZJH.ThreeLayer.Models { [Serializable()] public class UserInfo {//UserInfo對應表名,這個不硬性規定,但是約定俗成 public int ID { get; set; } public string UName { get; set; } public string UPwd { get; set; } //這里值得注意,在數據庫中是可以為null,在類型中對應int?可空類型 public int? UAge { get; set; } public DateTime SubTime { get; set; } public bool DelFalg { get; set; } public string Remark { get; set; } } }
有沒有沒看出什么,我們這里的思想很重要:我們將一張表 和 一個類 進行了對應:
- 類名我們對應表名
- 類屬性對應表字段,字段相應的類型對應
- 可空值類型的問題
每一張表可以對應出一個類,而表里面的數據就是實例出的一個個實體,表里面的數據集就相當於實體集合。
三、DAL層的理解
是主要的數據訪問層,在這一層中我們主要和數據進行交互,看下面的DAL代碼,我們這里主要放了一個方法,就是GetById,通過ID來獲得相應的實體數據。
ToModel這個方法比較靈活,在使用的時候。

//============================================================ //author:zhujinghui //============================================================ using System; using System.Collections.Generic; using System.Text; using System.Data; using System.Data.SqlClient; using ZJH.ThreeLayer.Models; namespace ZJH.ThreeLayer.DAL { public partial class UserInfoDAL { //這里我們設置一個方法,還有很多方法,比如增加刪除修改,通過ID得到相應的Model實體 public UserInfo GetByID(int iD) { string sql = "SELECT * FROM UserInfo WHERE ID = @ID"; using(SqlDataReader reader = SqlHelper.ExecuteDataReader(sql, new SqlParameter("@ID", iD))) { if (reader.Read()) { return ToModel(reader); } else { return null; } } } //通過一個SqlDataReader得到UserInfo 的 Model public UserInfo ToModel(SqlDataReader reader) { UserInfo userInfo = new UserInfo(); userInfo.ID = (int)ToModelValue(reader,"ID"); userInfo.UName = (string)ToModelValue(reader,"UName"); userInfo.UPwd = (string)ToModelValue(reader,"UPwd"); userInfo.UAge = (int?)ToModelValue(reader,"UAge"); userInfo.SubTime = (DateTime)ToModelValue(reader,"SubTime"); userInfo.DelFalg = (bool)ToModelValue(reader,"DelFalg"); userInfo.Remark = (string)ToModelValue(reader,"Remark"); return userInfo; } //這里是處理null值對應數據中的DBNull值 public object ToDBValue(object value) { if(value==null) { return DBNull.Value; } else { return value; } } //這里是處理數據中的DBNull值對應C#中德null值 public object ToModelValue(SqlDataReader reader,string columnName) { if(reader.IsDBNull(reader.GetOrdinal(columnName))) { return null; } else { return reader[columnName]; } } } }
相應的SqlHelper,SqlHelper在初學的時候是很重要的,需要仔細理解,這里是Dal層里面和底層數據庫交互最密切的類了,在里面主要是Ado.Net操作Sql數據庫的操作,這里面需要理解好后,封裝出dal層,dal層一般放增刪查改等方法。

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; using System.Data.SqlClient; using System.Data; using System.Reflection; namespace ZJH.ThreeLayer.DAL { class SqlHelper { /// <summary> /// 准備連接字符串 /// </summary> public static readonly string ConnStr = ConfigurationManager.ConnectionStrings["ConnStr"].ConnectionString; #region 1.0執行查詢多行語句 - 返回數據表 - public static DataTable ExecuteDataTable(string sqlSelectCmd, params SqlParameter[] parameters) /// <summary> /// 1.0執行查詢多行語句 - 返回數據表 /// </summary> /// <param name="sqlSelectCmd">查詢sql命令</param> /// <param name="parameters">查詢參數</param> /// <returns>DataTable查詢數據表</returns> private static DataTable ExecuteDataTable(string sqlSelectCmd, CommandType cmdType, params SqlParameter[] parameters) { //1.創建連接通道 using (SqlConnection conn = new SqlConnection(ConnStr)) { //2.創建適配器 SqlDataAdapter da = new SqlDataAdapter(sqlSelectCmd, conn); da.SelectCommand.CommandType = cmdType; //2.1設置查詢命令的參數 if (parameters != null && parameters.Length > 0) da.SelectCommand.Parameters.AddRange(parameters); //3.數據條 DataTable table = new DataTable(); //4.將查詢數據填充到數據表中 da.Fill(table); return table; } } /// <summary> /// 執行查詢多行語句 - 返回數據表 /// </summary> /// <param name="sqlSelectCmd">查詢sql命令</param> /// <param name="parameters">查詢參數</param> /// <returns>DataTable查詢數據表</returns> public static DataTable ExecuteDataTable(string sqlSelectCmd, params SqlParameter[] parameters) { return ExecuteDataTable(sqlSelectCmd, CommandType.Text, parameters); } /// <summary> /// 存儲過程執行查詢 - 返回單一數據表 /// </summary> /// <param name="procedureName">存儲過程名</param> /// <param name="parameters">參數,注意傳出參數的方向</param> /// <returns>結果集</returns> public static DataTable ExecuteDataTableSP(string procedureName, params SqlParameter[] parameters) { return ExecuteDataTable(procedureName, CommandType.StoredProcedure, parameters); } #endregion /// <summary> /// 1.1升級版-泛型版 ---- 執行查詢多行語句 - 返回list集合 /// </summary> /// <typeparam name="T2">泛型類型</typeparam> /// <param name="sqlSelectCmd">查詢sql命令</param> /// <param name="parameters">查詢參數</param> /// <returns>泛型集合</returns> private static List<T2> ExecuteList<T2>(string sqlSelectCmd, CommandType cmdType, params SqlParameter[] parameters) { //1.創建連接通道 using (SqlConnection conn = new SqlConnection(ConnStr)) { //2.創建適配器 SqlDataAdapter da = new SqlDataAdapter(sqlSelectCmd, conn); da.SelectCommand.CommandType = cmdType; //2.1設置查詢命令的參數 if (parameters != null && parameters.Length > 0) { da.SelectCommand.Parameters.AddRange(parameters); } //3.數據表 DataTable dt = new DataTable(); //4.將查詢結果填充到數據表中 if (conn.State == ConnectionState.Closed) { conn.Open(); } da.Fill(dt); //5.將DataTable轉成泛型集合List<T2> if (dt.Rows.Count > 0) { //6.創建泛型集合對象 List<T2> list = new List<T2>(); //7.遍歷數據表的每一行,將行數據存放到 實體對象中,並且添加到 泛型集合中list foreach (DataRow row in dt.Rows) { //7.1先獲得泛型的類型(里面包括類的所有信息----有什么屬性,有什么方法,有什么字段等等) Type t = typeof(T2); //7.2根據類型創建相應的該類型的對象 T2 model = (T2)Activator.CreateInstance(t); //7.3根據類型獲得該類型的所有屬性 PropertyInfo[] properties = t.GetProperties(); //7.4遍歷所有的屬性數組 foreach (PropertyInfo p in properties) { //7.4.1獲得所有屬性的名字 string colName = p.Name; //7.4.2根據列名,獲得當前循環行對應的值 object colValue = row[colName]; //7.4.3將 列值 賦給 model對象的p屬性 //考慮一下DBNULL的問題 p.SetValue(model, FromDbValue(colValue), null); } //7.5 將裝好 了行數據的 實體對象添加到 泛型集合中取 list.Add(model); } return list; } } return null; } /// <summary> /// 1.1升級版-泛型版 ---- 執行查詢多行語句 - 返回list集合 /// </summary> /// <typeparam name="T2">泛型類型</typeparam> /// <param name="sqlSelectCmd">查詢sql命令</param> /// <param name="parameters">查詢參數</param> /// <returns>泛型集合</returns> public static List<T2> ExecuteList<T2>(string sqlSelectCmd, params SqlParameter[] parameters) { return ExecuteList<T2>(sqlSelectCmd, CommandType.Text, parameters); } /// <summary> /// 存儲過程執行ExecuteList,獲得list,通過反射 /// </summary> public static List<T2> ExecuteListSP<T2>(string procedureName, params SqlParameter[] parameters) { return ExecuteList<T2>(procedureName, CommandType.StoredProcedure, parameters); } #endregion #region 2.執行查詢多行語句 -- 返回數據讀取器(輕量級) - public static SqlDataReader ExecuteDataReader(string sqlSelectCmd, params SqlParameter[] parameters) /// <summary> /// 2.執行查詢多行語句 -- 返回數據讀取器(輕量級) /// </summary> /// <param name="sqlSelectCmd">sql命令</param> /// <param name="parameters">命令參數</param> /// <returns>數據讀取器</returns> public static SqlDataReader ExecuteDataReader(string sqlSelectCmd, params SqlParameter[] parameters) { SqlConnection conn = null; SqlCommand cmd = null; try { //1.建立連接通道 conn = new SqlConnection(ConnStr); //1.1打開連接 conn.Open(); //2.創建命令對象 cmd = conn.CreateCommand(); //3.添加命令語句 cmd.CommandText = sqlSelectCmd; //4.添加命令參數 if (parameters != null && parameters.Length > 0) { cmd.Parameters.AddRange(parameters); } //5.創建讀取器(當關閉讀取器時候,會自動關閉連接通道) SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection); //7.返回讀取器 return dr; } catch (Exception ex) { conn.Dispose(); throw ex; } } #endregion #region 3.執行非查詢語句(增刪改) -- public static int ExecuteNonQuery(string sqlSelectCmd, params SqlParameter[] parameters) private static int ExecuteNonQuery(string sqlSelectCmd, CommandType cmdType, params SqlParameter[] parameters) { //1.打開連接通道 using (SqlConnection conn = new SqlConnection(ConnStr)) { conn.Open(); //2.建立SqlCommand連接 using (SqlCommand cmd = conn.CreateCommand()) { //3.添加命令語句 cmd.CommandText = sqlSelectCmd; cmd.CommandType = cmdType; //4.添加命令參數 if (parameters != null && parameters.Length > 0) cmd.Parameters.AddRange(parameters); //5.執行sql命令 int count = cmd.ExecuteNonQuery(); return count; } } } /// <summary> /// 執行非查詢語句(增刪改) /// </summary> /// <param name="sqlSelectCmd">sql命令</param> /// <param name="parameters">命令參數</param> /// <returns></returns> public static int ExecuteNonQuery(string sqlSelectCmd, params SqlParameter[] parameters) { return ExecuteNonQuery(sqlSelectCmd, CommandType.Text, parameters); } /// <summary> /// 存儲過程執行exetcuteNonQuery /// </summary> public static int ExecuteNonQuerySP(string procedureName, params SqlParameter[] parameters) { return ExecuteNonQuery(procedureName, CommandType.StoredProcedure, parameters); } #endregion #region 4.查詢sql命令,返回數據結果集的第一行第一列的單值 public static object ExecuteScalar(string sqlSelectCmd, params SqlParameter[] parameters) /// <summary> /// 4.查詢sql命令,返回數據結果集的第一行第一列的單值 /// </summary> /// <param name="sqlSelectCmd">sql命令</param> /// <param name="parameters">命令參數</param> /// <returns>單個值</returns> private static object ExecuteScalar(string sqlSelectCmd, CommandType cmdType, params SqlParameter[] parameters) { //1.建立連接通道 using (SqlConnection conn = new SqlConnection(ConnStr)) { //2.打開連接 conn.Open(); //3.建立Command using (SqlCommand cmd = conn.CreateCommand()) { //4.添加命令參數 if (parameters != null && parameters.Length > 0) cmd.Parameters.AddRange(parameters); //5.添加命令語句 cmd.CommandText = sqlSelectCmd; cmd.CommandType = cmdType; //6.執行查詢 object obj = cmd.ExecuteScalar(); return obj; } } } /// <summary> /// 查詢sql命令,返回數據結果集的第一行第一列的單值 /// </summary> /// <param name="sqlSelectCmd">sql命令</param> /// <param name="parameters">命令參數</param> /// <returns>單個值</returns> public static object ExecuteScalar(string sqlSelectCmd, params SqlParameter[] parameters) { return ExecuteScalar(sqlSelectCmd, CommandType.Text, parameters); } /// <summary> /// 存儲過程查詢sql命令,返回數據結果集的第一行第一列的單值 /// </summary> /// <param name="procedureName">存儲過程的名字</param> /// <param name="parameters">參數</param> /// <returns>單值</returns> public static object ExecuteScalarSP(string procedureName, params SqlParameter[] parameters) { return ExecuteScalar(procedureName, CommandType.StoredProcedure, parameters); } #endregion #region 5.0 從數據庫中讀取值,進行dbnull轉換成null public object FromDbValue(object obj) /// <summary> /// 從數據庫中讀取值,進行dbnull轉換成null /// </summary> /// <param name="obj">從數據庫讀取的值</param> /// <returns>返回值</returns> public static object FromDbValue(object obj) { if (obj == DBNull.Value) { return null; } else { return obj; } } #endregion #region 5.1 賦值寫入數據庫,進行null轉換成dbnull public object ToDbValue(object obj) /// <summary> /// 賦值寫入數據庫,進行null轉換成dbnull /// </summary> /// <param name="obj">要寫入數據庫中的對象</param> /// <returns>返回值</returns> public static object ToDbValue(object obj) { if (obj == null) { return DBNull.Value; } else { return obj; } } #endregion } }
四、BLL層代碼
這是邏輯層,負責將dal層傳過來的數據進行簡單的邏輯處理,可以參考代碼,主要是根據UI層需要進行什么邏輯處理,就在業務層進行處理,處理后根據結果進行邏輯判斷,返回UI層可以識別的返回值。

using System; using System.Collections.Generic; using System.Text; using ZJH.ThreeLayer.DAL; using ZJH.ThreeLayer.Models; namespace ZJH.ThreeLayer.BLL { public partial class UserInfoBLL { //這里是服務層,主要負責邏輯處理 public UserInfo GetByID(int iD) { //這里還沒有詳細的邏輯處理,后面別的還有詳細的處理 return new UserInfoDAL().GetByID(iD); } public bool DeleteByID(int id) { //關注這里,DeleteByID返回的數據是int,但是經過邏輯判斷的BLL層,返回的就是bool值,這只是簡單的邏輯判斷,需要根據你的業務返回對應的值 if(new UserInfoDAL().DeleteByID(id)>0) { return true; } else { return false; } } } }
五、MVC中對應的Model又指的是什么呢?
我們在MVC里面指的Model是涵蓋了BLL, DAL和Models,負責數據處理的多層結構,一般情況我們都會將MVC中Model抽出不同的幾個類庫,而不是單純地在一個文件夾中。