就個人而言,三層架構有點難理解,不知道該如何下手,各層與各層之間怎么調用
最近一直在研究三層架構,經過網上學習與多方打聽寫一下自己的心得。有不足之處,可以評論和私聊探討
言歸正傳:
三層架構(3-tier architecture) 通常意義上的三層架構就是將整個業務應用划分為:界面層(User Interface layer)、業務邏輯層(Business Logic Layer)、數據訪問層(Data access layer)。區分層次的目的即為了"高內聚低耦合"的思想。在軟件體系架構設計中,分層式結構是最常見,也是最重要的一種結構。微軟推薦的分層式結構一般分為三層,從下至上分別為:數據訪問層、業務邏輯層(又或稱為領域層)、表示層。
三層體系的應用程序將業務規則、數據訪問、合法性校驗等工作放到了中間層進行處理。通常情況下,客戶端不直接與數據庫進行交互,而是通過COM/DCOM通訊與中間層建立連接,再經由中間層與數據庫進行交互。
UI層:即表示層,就是展現給用戶看到的界面
BLL:即業務邏輯層,就是實現功能的,用來寫方法及其調用
DAL:即數據訪問層,也就是說,是對數據庫的操作,而不是數據,具體為業務邏輯層或表示層提供數據服務。說白了就是寫sql語句的;主要是存放對數據類的訪問,即對數據庫的添加、刪除、修改、更新等基本操作
除此三層外,聰明的人一定就還會說中間還有一個model(模型層)作為承載數據的媒介,供上面三個層引用。用來存儲實體類的,所以model實體類也很重要,
model:實體類庫,主要存放數據庫中的表字段
引用順序為UI引用BLL;BLL引用DAL;也可以間接引用
案例分析:
我們以一個登錄窗體作為案例
首先在數據庫建一張賬號用戶表,並填寫部分數據,用來測試
如圖
方案目錄結構
就個人而言還是從UI層開寫更好一點,因為UI層能直觀的看出你想實現什么功能效果,以便於下層該怎么下手
1.UI層
UI層做好,接下來最好做model實體類,實體類庫(Model),主要存放數據庫中的表字段。
后台代碼:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 登錄 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } //實例化model層中 userInfo類用於傳遞數據 Model.userInfo m_userInfo = new Model.userInfo(); //實例化BAL層中 userAccess方法銜接用戶輸入與數據庫匹配 BAL.userBLL b_userAccess = new BAL.userBLL(); private void Form1_Load(object sender, EventArgs e) { } private void button1_Click(object sender, EventArgs e) { //將用戶輸入的賬號密碼 賦值給userInfo類 username、psw屬性 m_userInfo.username = textBox1.Text.Trim().ToString(); m_userInfo.psw = textBox2.Text.Trim().ToString(); //如果BLL層中 useLogin調用返回記錄條數 大於1 則賬號密碼正確 if (b_userAccess.userLogin(m_userInfo) > 0) { MessageBox.Show("登錄成功"); } else { MessageBox.Show("登錄失敗"); } } } }
2.MODEL模型層
model層其實就相當於一個中轉站,用來存儲用到的數據,貫穿三層,數據的賦值及提取
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WindowsFormsApp4.MODEL { class userInfo //想創建一個用戶實體類 { //用get;set方法 public string username { get; set; } public string password { get; set; } } }
model實體類做好后接下來就可以寫DAL層了,DAL就是書寫sql語句操作數據庫,(就拿登錄窗體來說,我們在登錄時要輸入賬號和密碼,點擊登錄時就要與數據庫里的數據進行對比,只有驗證一致才能登陸成功。這時就要書寫sql語句取出數據庫里的數據與你在輸入框輸入的數據進行對比)
在寫DAL層時要寫一個SQLhelper幫助類(六面提供了很多方法與對象,極大的方便了開發人員的開發效率;其實一個sqlhelper幫助類可以放在任何地方調用,那小編就給大家一唄)

1 using System; 2 using System.Data; 3 using System.Collections; 4 using System.Configuration; //記得這個asp默認沒引用,你要去添加引用的程序集里引用進來 5 using System.Linq; 6 using System.Web; 7 using System.Xml.Linq; 8 using System.Data.SqlClient; 9 using System.Collections.Generic; 10 11 namespace UMS.DbHelper 12 { 13 /// <summary> 14 /// SQL數據庫操作類 15 /// </summary> 16 public class SQLHelper 17 { 18 private string strConn; 19 private SqlConnection sqlConn = null; 20 21 public SQLHelper() 22 { 23 strConn = ConfigurationManager.ConnectionStrings["UserData"].ConnectionString; 24 sqlConn = new SqlConnection(strConn); 25 26 } 27 28 /// <summary> 29 /// 打開數據庫連接 30 /// </summary> 31 private void OpenConn() 32 { 33 if (sqlConn != null && sqlConn.State == ConnectionState.Closed) 34 { 35 sqlConn.Open(); 36 } 37 } 38 39 /// <summary> 40 /// 關閉數據庫連接 41 /// </summary> 42 private void CloseConn() 43 { 44 if (sqlConn != null && sqlConn.State == ConnectionState.Open) 45 { 46 sqlConn.Close(); 47 } 48 } 49 /// <summary> 50 /// 構造操作命令 51 /// </summary> 52 /// <param name="cmdText">帶參命令</param> 53 /// <param name="param">參數數組</param> 54 /// <param name="values">參數值數組</param> 55 /// <returns></returns> 56 private SqlCommand CreateCommand(string cmdText, string[] param, object [] values) 57 { 58 SqlCommand myCmd = new SqlCommand(cmdText,sqlConn); 59 for (int i = 0; i < param.Length; i++) 60 { 61 myCmd.Parameters.AddWithValue(param[i],values[i]); 62 } 63 return myCmd; 64 } 65 /// <summary> 66 /// 根據SQL指令返回相應查詢閱讀器,在閱讀器使用完后請及時關閉 67 /// </summary> 68 /// <param name="cmdText">查詢語句</param> 69 /// <param name="param">參數列表,無參可設置為null</param> 70 /// <param name="values">參數值列表,只有當參數不為空時有效</param> 71 /// <returns></returns> 72 public SqlDataReader ExecuteReader(string cmdText,string [] param,object [] values) 73 { 74 OpenConn(); 75 SqlCommand myCmd; 76 if (param != null) 77 { 78 myCmd = this.CreateCommand(cmdText, param, values); 79 } 80 else 81 { 82 myCmd = new SqlCommand(cmdText,sqlConn); 83 } 84 return myCmd.ExecuteReader(CommandBehavior.CloseConnection); 85 } 86 87 /// <summary> 88 /// 根據存儲過程返回相應查詢閱讀器,在閱讀器使用完后請及時關閉 89 /// </summary> 90 /// <param name="cmdText">存儲過程名</param> 91 /// <param name="parms">參數列表</param> 92 /// <returns></returns> 93 public SqlDataReader ExecuteReaderBySP(string cmdText, SqlParameter[] parms) 94 { 95 OpenConn(); 96 SqlCommand myCmd = new SqlCommand(cmdText, sqlConn); 97 myCmd.CommandType = CommandType.StoredProcedure; 98 if (parms != null) 99 { 100 myCmd.Parameters.AddRange(parms); 101 } 102 return myCmd.ExecuteReader(CommandBehavior.CloseConnection); 103 } 104 105 /// <summary> 106 /// 根據SQL指令返回受影響行數,主要用於數據庫的更新、插入、刪除等操作 107 /// </summary> 108 /// <param name="cmdText">sql命令語句</param> 109 /// <param name="param">參數數組,若沒有參數可以設置為空</param> 110 /// <param name="values">參數值數組,只有當param不為空時有效</param> 111 /// <returns></returns> 112 public int ExecuteNoneQuery(string cmdText, string[] param, object[] values) 113 { 114 OpenConn(); 115 SqlCommand myCmd; 116 if (param != null) 117 { 118 myCmd = this.CreateCommand(cmdText, param, values); 119 } 120 else 121 { 122 myCmd = new SqlCommand(cmdText,sqlConn); 123 } 124 try 125 { 126 return myCmd.ExecuteNonQuery(); 127 } 128 catch (Exception ex) 129 { 130 throw ex; 131 } 132 finally 133 { 134 CloseConn(); 135 } 136 } 137 138 /// <summary> 139 /// 根據SQL指令返回第一行第一列結果 140 /// </summary> 141 /// <param name="cmdText">sql命令語句</param> 142 /// <param name="param">參數數組,若沒有參數可以設置為空</param> 143 /// <param name="values">參數值數組,只有當param不為空時有效</param> 144 /// <returns></returns> 145 public object ExecuteScalar(string cmdText, string[] param, object[] values) 146 { 147 OpenConn(); 148 SqlCommand myCmd; 149 if (param != null) 150 { 151 myCmd = this.CreateCommand(cmdText, param, values); 152 } 153 else 154 { 155 myCmd = new SqlCommand(cmdText,sqlConn); 156 } 157 try 158 { 159 return myCmd.ExecuteScalar(); 160 } 161 catch (Exception ex) 162 { 163 throw ex; 164 } 165 finally 166 { 167 CloseConn(); 168 } 169 } 170 /// <summary> 171 /// 帶事務執行存儲過程,該方法主要用於執行用於數據維護類的存儲過程執行 172 /// </summary> 173 /// <param name="cmdText">存儲過程名稱</param> 174 /// <param name="parms">SQL參數數組</param> 175 public int ExecuteNoneQueryBySP(string cmdText, SqlParameter[] parms) 176 { 177 OpenConn(); 178 SqlTransaction tran = sqlConn.BeginTransaction(); 179 SqlCommand myCmd = new SqlCommand(cmdText, sqlConn); 180 myCmd.CommandType = CommandType.StoredProcedure; 181 if (parms != null) 182 { 183 myCmd.Parameters.AddRange(parms); 184 } 185 myCmd.Transaction = tran; 186 try 187 { 188 int result=myCmd.ExecuteNonQuery(); 189 tran.Commit(); 190 return result; 191 } 192 catch (Exception ex) 193 { 194 tran.Rollback(); 195 throw ex; 196 } 197 finally 198 { 199 CloseConn(); 200 } 201 } 202 /// <summary> 203 /// 根據命令語句返回數據集 204 /// </summary> 205 /// <param name="cmdText">命令語句</param> 206 /// <param name="param">參數數組,若沒有參數可以設置為空</param> 207 /// <param name="values">參數值數組,只有當param不為空時有效</param> 208 /// <returns></returns> 209 public DataSet FillDataSet(string cmdText, string[] param, object[] values) 210 { 211 OpenConn(); 212 SqlCommand myCmd; 213 if (param != null) 214 { 215 myCmd = this.CreateCommand(cmdText, param, values); 216 } 217 else 218 { 219 myCmd = new SqlCommand(cmdText,sqlConn); 220 } 221 SqlDataAdapter myAdp = new SqlDataAdapter(myCmd); 222 DataSet ds = new DataSet(); 223 try 224 { 225 myAdp.Fill(ds); 226 return ds; 227 } 228 catch (Exception ex) 229 { 230 throw ex; 231 } 232 finally 233 { 234 CloseConn(); 235 } 236 } 237 238 /// <summary> 239 /// 執行特定存儲過程並返回查詢后的數據結果,該方法用於執行查詢類的存儲過程 240 /// </summary> 241 /// <param name="cmdText">存儲過程名</param> 242 /// <param name="parms">SQL參數數組,若沒有參數可以設置為空</param> 243 /// <returns></returns> 244 public DataSet FillDataSetBySP(string cmdText, SqlParameter[] parms) 245 { 246 OpenConn(); 247 SqlCommand myCmd = new SqlCommand(cmdText, sqlConn); 248 myCmd.CommandType = CommandType.StoredProcedure; 249 if (parms != null) 250 { 251 myCmd.Parameters.AddRange(parms); 252 } 253 SqlDataAdapter myAdp = new SqlDataAdapter(myCmd); 254 DataSet ds = new DataSet(); 255 try 256 { 257 myAdp.Fill(ds); 258 return ds; 259 } 260 catch (Exception ex) 261 { 262 throw ex; 263 } 264 finally 265 { 266 CloseConn(); 267 } 268 } 269 /// <summary> 270 /// 執行存儲過程返回輸出參數 271 /// </summary> 272 /// <param name="cmdText">存儲過程名</param> 273 /// <param name="parms">參數數組</param> 274 /// <returns>包含所有輸出值的ArrayList</returns> 275 public ArrayList ExecuteSp(string cmdText, SqlParameter[] parms) 276 { 277 OpenConn(); 278 SqlCommand myCmd = new SqlCommand(cmdText, sqlConn); 279 myCmd.CommandType = CommandType.StoredProcedure; 280 if (parms != null) 281 { 282 myCmd.Parameters.AddRange(parms); 283 } 284 try 285 { 286 myCmd.ExecuteNonQuery(); 287 ArrayList al = new ArrayList(); 288 for (int i = 0; i < parms.Length; i++) 289 { 290 if (parms[i].Direction == ParameterDirection.Output) 291 { 292 al.Add(parms[i]); 293 } 294 } 295 return al; 296 } 297 catch (Exception ex) 298 { 299 throw ex; 300 } 301 finally 302 { 303 CloseConn(); 304 } 305 } 306 307 #region 批處理操作 308 /// <summary> 309 /// 批量數據導入操作 310 /// </summary> 311 /// <param name="dt">要批量導入的數據表</param> 312 /// <param name="destTableName">目標表名</param> 313 /// <param name="columnMappings">列映射集合</param> 314 public void BulkInsert(DataTable dt, string destTableName, List<SqlBulkCopyColumnMapping> columnMappings) 315 { 316 SqlBulkCopy bulkCopy = new SqlBulkCopy(this.sqlConn); 317 bulkCopy.DestinationTableName = destTableName; 318 bulkCopy.BatchSize = dt.Rows.Count; 319 foreach (SqlBulkCopyColumnMapping map in columnMappings) 320 { 321 bulkCopy.ColumnMappings.Add(map); 322 } 323 try 324 { 325 OpenConn(); 326 bulkCopy.WriteToServer(dt); 327 } 328 catch (Exception ex) 329 { 330 throw ex; 331 } 332 finally 333 { 334 this.CloseConn(); 335 bulkCopy.Close(); 336 } 337 } 338 #endregion 339 } 340 }
類里邊不是有個數據庫訪問方法,那里是在配置文件里配置了數據庫連接字符串,所以就沒有在方法里寫連接字符串
例如
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> </startup> <connectionStrings> <add name="UserData" connectionString="Data Source=.;Initial Catalog=賬號表;User ID=sa;Password=密碼"/> </connectionStrings> </configuration>
SqlHelper
私有方法有四個,AssignParameterValues方法有一個重載:
AttachParameters:添加參數數組到指定的SqlCommand中
AssignParameterValues:為SqlParameters(參數)數組賦值
PrepareCommand:用於對SqlCommand(命令)的屬性(如連接、事務環境等)進行初始化。
公有方法有十三個:這當中每個查詢數據庫的方法用到了大量的重載,每個方法用到了八個左右的重載。
ExecuteNonQuery
此方法用於執行不返回任何行或值的命令。這些命令通常用於執行數據庫更新,但也可用於返回存儲過程的輸出參數。
ExecuteDataset
此方法返回DataSet對象,該對象包含由某一命令返回的結果集。
ExecuteReader
此方法用於返回SqlDataReader對象,該對象包含由某一命令返回的結果集。
ExecuteScalar
此方法返回一個值。該值始終是該命令返回的第一行的第一列。
ExecuteXmlReader
此方法返回 FOR XML 查詢的 XML 片段。
FillDataset
此方法向DataSet填充數據。
UpdateDataset
此方法用於執行向DataSet增、刪、改的命令。
CreateCommand
此方法用於創建SqlCommand。
3.DAL層
建好第一步就先把sqlhelper類和model實體類引進來(以創建對象的方式)
我這里只寫了登錄時用到的,別的就要靠你自己摸索了

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DAL { public class userDAL { //實例化DBbase 對象 DBbase db = new DBbase(); //用戶登錄的方法 public int userLogin(string name, string psw) { string strsql = "select * from users where username = '" + name + "' and password = '" + psw + "'"; return db.returnRowCount(strsql); } } }
登錄的sqlhelper類,在這里叫DBbase
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Data; using System.Data.SqlClient; namespace DAL { public class DBbase { //讀取配置文件 連接數據庫語句 public static string strCon = System.Configuration.ConfigurationManager.ConnectionStrings["TestDB"].ConnectionString; //public static string strCon = "Data Source=.;Initial Catalog=threeLayer;Persist Security Info=True;User ID=sa;Password=123"; //實例化連接對象 con SqlConnection con = new SqlConnection(strCon); //檢測連接是否打開 public void chkConnection() { if (this.con.State == ConnectionState.Closed) { this.con.Open(); } } //執行語句,返回該語句查詢的數據行的總行數 public int returnRowCount(string strSQL) { chkConnection(); try { SqlDataAdapter da = new SqlDataAdapter(strSQL, con); DataSet ds = new DataSet(); da.Fill(ds); return ds.Tables[0].Rows.Count; } catch { return 0; } } } }
4.BLL層
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BAL { public class userBLL { DAL.userDAL d_userAccess = new DAL.userDAL(); public int userLogin(Model.userInfo m_userInfo)//把model層的值傳過來進行比對 { return d_userAccess.userLogin(m_userInfo.username, m_userInfo.psw);//如果有返回值則登錄成功 } } }
到這里就大功告成了,登錄效果