[ASP.NET Core MVC][Vue3][Dapper] 後端資料庫底層架構建置 #CH3
在前兩個教學章節我完成了新增、修改、刪除及查詢的資料庫語法,而我教學的語法會放在第一層全部展示,這適合教學好理解,但不適合專案使用,不然會大量出現相同功能的程式碼,而在這個章節會做一次後端資料庫物件導向化的底層架構建置。
底層架構建置是一種方法抽離的過程,將相同功能的程式碼抽離至方法內,只提供方法呼叫,可以簡化重複撰寫的程式碼。
擁有底層架構的專案,在撰寫程式碼就會減短許多,這也是物件導向的精神,將相同功能封裝起來,只提供對外呼叫的方法。
接下來這個範例會將前面兩個章節的範例,將資料庫執行的共用的部份,封裝到底層的類別庫,實現資料庫底層類別庫的製作。
在開始之前,建議你先實作完第一篇與第二篇的教學範例,會比較好理解底層架構的建置過程。
Contents
建立底層類別庫
底層類別庫是適用在多數方案引用的專案,編譯後為一個 .dll 檔案,所以我們在現有的方案加入一個新專案,輸出類型為「類別庫」。
執行「解決方案 > 加入 > 新增專案」。
在「新增專案」內選擇「類別庫」。
輸入專案名稱「ProjectLibrary」,架構為「.NET 6.0」 。
建立專案後,可以移除預設的 Class1 類別,然後新增一個資料夾,名為「Base」。
建立業務邏輯底層類別
在「Base」內新增一類別,名稱為「BusinessBase」,此類別為處理業務邏輯與資料庫互動的類別,之後給業務邏輯類別繼承使用,可以將許多共用方法放在此處。
在 BusinessBase 內貼上以下語法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
using ProjectLibrary.DB; using System.Collections; using System.Dynamic; using System.Text; namespace ProjectLibrary.Base { public class BusinessBase { #region 屬性 public Hashtable UsedParamName = new Hashtable(); public DBManager dbManager; public Hashtable htColumn = new Hashtable(); public int TotalRowCount = 0; #endregion #region 建構子 public BusinessBase(DBManager _dbManager) { dbManager = _dbManager; } #endregion #region 方法 /// <summary> /// 產生 SQL 條件語法 /// </summary> /// <param name="sbWhere"></param> /// <param name="param"></param> /// <param name="column"></param> /// <param name="oper"></param> /// <param name="value"></param> public void GenWhere(StringBuilder sbWhere, dynamic param, string column, string oper, object value) { if (value != null && value.ToString() != "") { switch (oper.ToUpper().Trim()) { case "=": case ">": case "<": case ">=": case "<=": case "<>": sbWhere.Append(" AND " + column + " " + oper + " @" + CreateParamName(column) + " "); AddProperty(param, CreateParamName(column), value); break; case "%LIKE%": sbWhere.Append(" AND " + column + " " + oper + " @" + CreateParamName(column) + " "); AddProperty(param, CreateParamName(column), "%" + value + "%"); break; } } } /// <summary> /// 產生 SQL 新增語法 /// </summary> /// <param name="sbColumn"></param> /// <param name="sbValue"></param> /// <param name="param"></param> /// <param name="column"></param> /// <param name="value"></param> public void GenInsert(StringBuilder sbColumn, StringBuilder sbValue, dynamic param, string column, object value) { if (value != null && value.ToString() != "") { if (sbColumn.Length > 0) sbColumn.Append(", "); sbColumn.Append(column); if (sbValue.Length > 0) sbValue.Append(", "); sbValue.Append("@" + CreateParamName(column)); AddProperty(param, CreateParamName(column), value); } } /// <summary> /// 產生 SQL 修改語法 /// </summary> /// <param name="sbColumn"></param> /// <param name="param"></param> /// <param name="column"></param> /// <param name="value"></param> public void GenUpdate(StringBuilder sbColumn, dynamic param, string column, object value) { if (value != null && value.ToString() != "") { if (sbColumn.Length > 0) sbColumn.Append(", "); sbColumn.Append(column + " = @" + CreateParamName(column)); AddProperty(param, CreateParamName(column), value); } } /// <summary> /// 建立參數名稱 /// </summary> /// <param name="column"></param> /// <returns></returns> private string CreateParamName(string column) { if (UsedParamName.Contains(column) == false) { UsedParamName.Add(column, column + "_" + UsedParamName.Count); } return UsedParamName[column].ToString(); } /// <summary> /// 附加物件屬性 /// </summary> /// <param name="expando"></param> /// <param name="propertyName"></param> /// <param name="propertyValue"></param> private void AddProperty(ExpandoObject expando, string propertyName, object propertyValue) { var expandoDict = expando as IDictionary<string, object>; if (expandoDict.ContainsKey(propertyName)) expandoDict[propertyName] = propertyValue; else expandoDict.Add(propertyName, propertyValue); } /// <summary> /// 重設欄位 /// </summary> public void ResetColumn() { htColumn.Clear(); } #endregion #region 欄位 public int PageNo { get { return Convert.ToInt32(htColumn["PageNo"]); } set { htColumn["PageNo"] = value; } } public int PageSize { get { return Convert.ToInt32(htColumn["PageSize"]); } set { htColumn["PageSize"] = value; } } #endregion } } |
此類別的主要目的是動態產生可執行的 SQL,當傳入欄位有值時,才動態產生 SQL 語法。
建立資料庫管理類別
接著新增第二個類別,在「ProjectLibrary」專案下新增一個資料夾名為「DB」,然後在「DB」資料夾下新增一類別,名稱為「DBManager」。
在 DBManager 內貼上以下語法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
using System.Data; using System.Text; using Dapper; namespace ProjectLibrary.DB { public class DBManager { #region 屬性 string connStr = "";//資料庫連線字串 List<DBConn> conns = new List<DBConn>(); //資料庫連線集合 #endregion #region 建構子 public DBManager(string _connStr) { connStr = _connStr; } #endregion #region 方法 /// <summary> /// 取得資料庫連線 /// </summary> /// <param name="transName">交易名稱</param> /// <returns></returns> public DBConn GetConn(string transName) { DBConn? dbConn = conns.Where(x => x.transName == transName).FirstOrDefault(); if (dbConn == null) { dbConn = new DBConn(transName, connStr); dbConn.conn.Open(); if (transName != "") { dbConn.trans = dbConn.conn.BeginTransaction(transName); } conns.Add(dbConn); } return dbConn; } /// <summary> /// 取得資料 /// </summary> /// <param name="sql"></param> /// <param name="param"></param> /// <returns></returns> public DataTable GetData(string sql, object param, int pageNo, int pageSize, ref int _TotalRowCount) { DBConn dbConn = GetConn(""); // 還原可執行 SQL (Debug 與 Log 使用) string sqlLog = sql; var expandoDict = param as IDictionary<string, object>; foreach (KeyValuePair<string, object> kv in expandoDict) { sqlLog = sqlLog.Replace("@" + kv.Key, "'" + kv.Value + "'"); } IDataReader list; DataTable dt = new DataTable(); if (pageNo > 0) { // 分頁處理 // 取得總筆數 string orderBy = ""; string totalRowSql = sql; if (totalRowSql.ToUpper().IndexOf("ORDER BY") > -1) { orderBy = totalRowSql.Substring(sql.ToUpper().LastIndexOf("ORDER BY")); totalRowSql = totalRowSql.Replace(orderBy, ""); } totalRowSql = "SELECT COUNT(*) AS CNT FROM (" + totalRowSql + ") CNT_TABLE"; var rowCnt = dbConn.conn.Query(totalRowSql, param); foreach (var item in rowCnt) { _TotalRowCount = item.CNT; } // 取得分頁 SQL int startRow = ((pageNo - 1) * pageSize) + 1; int endRow = (startRow + pageSize) - 1; orderBy = sql.Substring(sql.ToString().ToUpper().LastIndexOf("ORDER BY")); sql = sql.Replace(orderBy, ""); // 去除 Order by 別名 orderBy = orderBy.ToUpper().Replace("ORDER BY", ""); StringBuilder newOrderBy = new StringBuilder(); int index = 0; string[] orderBys = orderBy.Split(','); for (int i = 0; i < orderBys.Length; i++) { if (newOrderBy.Length > 0) { newOrderBy.Append(","); } string ob = orderBys[i]; index = ob.IndexOf('.'); if (index > -1) { newOrderBy.Append(ob.Substring(index + 1)); } else { newOrderBy.Append(ob); } } newOrderBy.Insert(0, "ORDER BY "); sql = string.Concat( new object[] { "SELECT * FROM (SELECT *, ROW_NUMBER() OVER (", newOrderBy.ToString(), ") AS RCOUNT FROM (", sql, ") PAGE_SQL ) PAGE_SQL2 WHERE PAGE_SQL2.RCOUNT BETWEEN " , startRow, " AND ", endRow, " ", newOrderBy.ToString() }); list = dbConn.conn.ExecuteReader(sql, param); dt.Load(list); } else { // 直接查詢 SQL list = dbConn.conn.ExecuteReader(sql, param); dt.Load(list); _TotalRowCount = dt.Rows.Count; } return dt; } /// <summary> /// 執行新增 /// </summary> /// <param name="tableName"></param> /// <param name="columnSql"></param> /// <param name="valueSql"></param> /// <param name="param"></param> /// <returns></returns> public int Insert(string tableName, string columnSql, string valueSql, object param) { // 取得連線 DBConn dbConn = GetConn("DefaultTrans"); // 執行 SQL string sql = "INSERT INTO " + tableName + "(" + columnSql + ") VALUES (" + valueSql + ")"; // 還原可執行 SQL (Debug 與 Log 使用) string sqlLog = sql; var expandoDict = param as IDictionary<string, object>; foreach (KeyValuePair<string, object> kv in expandoDict) { sqlLog = sqlLog.Replace("@" + kv.Key, "'" + kv.Value + "'"); } int ret = dbConn.conn.Execute(sql, param, dbConn.trans); return ret; } /// <summary> /// 執行修改 /// </summary> /// <param name="tableName"></param> /// <param name="columnSql"></param> /// <param name="whereSql"></param> /// <param name="param"></param> /// <returns></returns> public int Update(string tableName, string columnSql, string whereSql, object param) { // 取得連線 DBConn dbConn = GetConn("DefaultTrans"); // 執行 SQL string sql = "UPDATE " + tableName + " SET " + columnSql + " WHERE 1=1 " + whereSql; // 還原可執行 SQL (Debug 與 Log 使用) string sqlLog = sql; var expandoDict = param as IDictionary<string, object>; foreach (KeyValuePair<string, object> kv in expandoDict) { sqlLog = sqlLog.Replace("@" + kv.Key, "'" + kv.Value + "'"); } int ret = dbConn.conn.Execute(sql, param, dbConn.trans); return ret; } /// <summary> /// 執行刪除 /// </summary> /// <param name="tableName"></param> /// <param name="columnSql"></param> /// <param name="whereSql"></param> /// <param name="param"></param> /// <returns></returns> public int Delete(string tableName, string whereSql, object param) { // 取得連線 DBConn dbConn = GetConn("DefaultTrans"); // 執行 SQL string sql = "DELETE FROM " + tableName + " WHERE 1=1 " + whereSql; // 還原可執行 SQL (Debug 與 Log 使用) string sqlLog = sql; var expandoDict = param as IDictionary<string, object>; foreach (KeyValuePair<string, object> kv in expandoDict) { sqlLog = sqlLog.Replace("@" + kv.Key, "'" + kv.Value + "'"); } int ret = dbConn.conn.Execute(sql, param, dbConn.trans); return ret; } /// <summary> /// 提交異動 /// </summary> public void Commit() { foreach (DBConn dbConn in conns) { if (dbConn.trans != null) { dbConn.trans.Commit(); dbConn.trans.Dispose(); dbConn.trans = null; } } } /// <summary> /// 回復異動 /// </summary> public void Rollback() { foreach (DBConn dbConn in conns) { if (dbConn.trans != null) { dbConn.trans.Rollback(); dbConn.trans.Dispose(); dbConn.trans = null; } } } /// <summary> /// 結束連線 /// </summary> public void Close() { foreach (DBConn dbConn in conns) { if (dbConn.conn != null) { dbConn.conn.Close(); dbConn.conn.Dispose(); dbConn.conn = null; } } } #endregion } } |
與資料庫互動使用微型 ORM 套件 Dapper,這個是新專案,所以要再次安裝 Dapper 套件。
這裡有一個新類別「DBConn」,所以在「DB」目錄下新增此類別,名為「DBConn」,然後貼上以下語法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
using System.Data.SqlClient; namespace ProjectLibrary.DB { public class DBConn { #region 屬性 public string transName = ""; public SqlConnection? conn; public SqlTransaction? trans; #endregion #region 建構子 public DBConn(string _transName, string connStr) { transName = _transName; conn = new SqlConnection(connStr); } #endregion } } |
建立專案底層 Controller
接著這裡回到我們的 MVC 專案上,我們要針對 Controller 建立基礎類別,讓所有的 Controller 繼承此類別。
在 Controllers 目錄下新增類別,名稱為「BaseController」,此類別要繼承 Controller。
BaseController 類別要覆寫底層的一些生命週期事件,讓我們專案在預設執行時,就載入一些設定,並在生命週期事件完成基礎工作。
在 BaseController.cs 類別內,加入以下語法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using ProjectLibrary.DB; using System.Data; using System.Reflection; using TeachAnnouncement.Models; namespace TeachAnnouncement.Controllers { public class BaseController : Controller { #region 屬性 public DBManager dbManager; #endregion #region 方法 /// <summary> /// Action 執行前動作 /// </summary> /// <param name="context"></param> public override void OnActionExecuting(ActionExecutingContext context) { IConfiguration Config = new ConfigurationBuilder().AddJsonFile("appSettings.json").Build(); // 資料庫連線字串 string connStr = Config.GetConnectionString("SqlServer"); dbManager = new DBManager(connStr); base.OnActionExecuting(context); } /// <summary> /// Action 執行後動作 /// </summary> /// <param name="context"></param> public override void OnActionExecuted(ActionExecutedContext context) { if (context.Exception != null) { // 當有錯誤時,執行回復資料庫 dbManager.Rollback(); } else { // 當正常執行時,則執行資料庫提交動作 dbManager.Commit(); } // 關閉資料庫連線 dbManager.Close(); base.OnActionExecuted(context); } /// <summary> /// 將 DataRow 資料綁定到 Model 裡面 /// </summary> /// <param name="dr"></param> /// <param name="column"></param> /// <param name="row"></param> /// <returns></returns> public BaseModel BindModelByDataRow(DataRow dr, DataColumnCollection column, BaseModel model) { Type typeM = model.GetType(); PropertyInfo? propM; for (int i = 0; i < column.Count; i++) { string fieldName = column[i].ColumnName; propM = typeM.GetProperty(fieldName); if (propM != null) { string value = ""; if (dr[i] != null || Convert.IsDBNull(dr[i]) == false) { value = dr[i].ToString(); } propM.SetValue(model, value); } } return model; } /// <summary> /// 計算分頁 /// </summary> /// <param name="model"></param> /// <param name="TotalRowCount"></param> /// <returns></returns> public PaginationModel PreparePage(PaginationModel model, int TotalRowCount) { List<string> pages = new List<string>(); int pageStart = ((model.pageNo - 1) / 10) * 10; model.totalCount = TotalRowCount; model.totalPage = Convert.ToInt16(Math.Ceiling( double.Parse(model.totalCount.ToString()) / double.Parse(model.pageSize.ToString()) )); if (model.pageNo > 10) pages.Add("<<"); if (model.pageNo > 1) pages.Add("<"); for (int i = 1; i <= 10; ++i) { if (pageStart + i > model.totalPage) break; pages.Add((pageStart + i).ToString()); } if (model.pageNo < model.totalPage) pages.Add(">"); if ((pageStart + 10) < model.totalPage) pages.Add(">>"); model.pages = pages; return model; } #endregion } } |
類別用到的 DBManager 來至 ProjectLibrary 類別庫,所以我們需要把 ProjectLibrary 類別庫引用到 MVC 裡面來。
在專案的「相依性 > 新增專案參考」,可以載入類別庫,再將 ProjectLibrary 專案入載即可。
建立專案底層 Model
建立底層 Model 目的讓所有的 ViewModel 都繼承此類別,這樣有助於將共用欄位放至底層,而不用在每一個 ViewModel 都宣告相同欄位。
例如呼叫 Action 時,回傳的 Model 裡面都包含了「ErrMsg
」欄位,這是讓 View 前端檢查是否有錯誤訊息的欄位,像這種共用欄位就可以放在底層 Model 裡面。
在 BaseController 類別用中到的 PaginationModel
原本寫在 \Models\AdmAnnoViewModel.cs 裡面,此類別也屬於共用 Model 的範圍,所以適合放在底層 Model 裡面。
可以建立一個共用的 Model 類別,在「Models > 加入 > 類別」,建立新類別,名稱為「BaseModel」。
這樣就可以把共用的 Model 放在這裡,可以貼上以下語法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
namespace TeachAnnouncement.Models { /// <summary> /// ViewModel 共用 /// </summary> public class BaseModel { public string? ErrMsg { get; set; } } /// <summary> // 分頁 Model /// </summary> public class PaginationModel { public List<string> pages { get; set; } public int pageNo { get; set; } public int pageSize { get; set; } public int totalPage { get; set; } public int totalCount { get; set; } } } |
做到這裡已經完成基礎的底層建置工作,接著就要著手建立業務邏輯的部份。
建立業務邏輯類別
這裡的業務邏輯指的是專案內的功能實作,實作內容大多是資料庫的執行動作,例如會員功能會建立一個 BusMember
類別專門處理會員相關的工作,而在我們這次的範例是網站公告,所以我會建立一個 BusAnnouncement
類別,將公告相關業務邏輯放在這裡。
在 MVC 專案內,我們先建立一資料夾,取名為「Business」,然後在裡面新增一類別,名為「BusAnnouncement」,BusAnnouncement 類別會繼承我們剛剛建立的 BusinessBase。
然後在 BusAnnouncement 類別,加入以下語法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
using ProjectLibrary.Base; using ProjectLibrary.DB; using System.Data; using System.Dynamic; using System.Text; namespace TeachAnnouncement.Business { public class BusAnnouncement : BusinessBase { #region 建構子 public BusAnnouncement(DBManager _dbManager) : base(_dbManager) { } #endregion #region 方法 /// <summary> /// 取得公告 /// </summary> /// <returns></returns> public DataTable GetAnno() { StringBuilder sql = new StringBuilder(); sql.Append("SELECT Pkey, CONVERT(varchar(12) , AnnoDate, 111 ) as AnnoDate, AnnoSubject, AnnoContent, AnnoStatus, Case AnnoStatus when '1' then '顯示' when '0' then '隱藏' end As AnnoStatusName "); sql.Append("FROM Announcement "); sql.Append("WHERE 1=1 "); StringBuilder sbWhere = new StringBuilder(); dynamic param = new ExpandoObject(); // 動態組合 SQL 條件 GenWhere(sbWhere, param, "AnnoSubject", "%LIKE%", AnnoSubject); GenWhere(sbWhere, param, "AnnoStatus", "=", AnnoStatus); sql.Append(sbWhere); sql.Append("ORDER BY AnnoDate desc, AnnoStatus"); // 執行查詢 DataTable dt = dbManager.GetData(sql.ToString(), param, PageNo, PageSize, ref TotalRowCount); ResetColumn(); return dt; } /// <summary> /// 新增公告 /// </summary> /// <returns></returns> public int InsertAnno() { StringBuilder sbColumn = new StringBuilder(); StringBuilder sbValue = new StringBuilder(); dynamic param = new ExpandoObject(); // 動態組合 SQL 欄位 GenInsert(sbColumn, sbValue, param, "AnnoDate", AnnoDate); GenInsert(sbColumn, sbValue, param, "AnnoSubject", AnnoSubject); GenInsert(sbColumn, sbValue, param, "AnnoContent", AnnoContent); GenInsert(sbColumn, sbValue, param, "AnnoStatus", AnnoStatus); // 執行新增 int cnt = dbManager.Insert("Announcement", sbColumn.ToString(), sbValue.ToString(), param); ResetColumn(); return cnt; } /// <summary> /// 修改公告 /// </summary> /// <returns></returns> public int UpdateAnno() { StringBuilder sbColumn = new StringBuilder(); StringBuilder sbWhere = new StringBuilder(); dynamic param = new ExpandoObject(); // 動態組合 SQL 欄位 GenUpdate(sbColumn, param, "AnnoDate", AnnoDate); GenUpdate(sbColumn, param, "AnnoSubject", AnnoSubject); GenUpdate(sbColumn, param, "AnnoContent", AnnoContent); GenUpdate(sbColumn, param, "AnnoStatus", AnnoStatus); // 動態組合 SQL 條件 GenWhere(sbWhere, param, "Pkey", "=", this.Pkey); // 執行修改 int cnt = dbManager.Update("Announcement", sbColumn.ToString(), sbWhere.ToString(), param); ResetColumn(); return cnt; } /// <summary> /// 刪除公告 /// </summary> /// <returns></returns> public int DeleteAnno() { StringBuilder sbWhere = new StringBuilder(); dynamic param = new ExpandoObject(); // 動態組合 SQL 條件 GenWhere(sbWhere, param, "Pkey", "=", this.Pkey); // 執行修改 int cnt = dbManager.Delete("Announcement", sbWhere.ToString(), param); ResetColumn(); return cnt; } #endregion #region 欄位 public object Pkey { get { return htColumn["Pkey"]; } set { htColumn["Pkey"] = value; } } public object AnnoSubject { get { return htColumn["AnnoSubject"]; } set { htColumn["AnnoSubject"] = value; } } public object AnnoStatus { get { return htColumn["AnnoStatus"]; } set { htColumn["AnnoStatus"] = value; } } public object AnnoDate { get { return htColumn["AnnoDate"]; } set { htColumn["AnnoDate"] = value; } } public object AnnoContent { get { return htColumn["AnnoContent"]; } set { htColumn["AnnoContent"] = value; } } #endregion } } |
這裡寫了針對資料庫的執行動作,通常一個 Table 只需要提供 4 種方法 (新增、修改、刪除、查詢) 即可。
修改 ViewModel 繼承 BaseModel
我們剛剛新增了 BaseModel 類別,這裡要開啟 \Models\AdmAnnoViewModel.cs,將裡面原本的 Model 全部都繼承 BaseModel 類別,同時移除不需要存在的 PaginationModel
。
可以貼上以下語法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
using System.ComponentModel.DataAnnotations; namespace TeachAnnouncement.Models { public class AdmAnnoViewModel { public class QueryIn : BaseModel { public string AnnoSubject { get; set; } public string AnnoStatus { get; set; } public PaginationModel pagination { get; set; } } public class QueryOut : BaseModel { public List<AnnoModel> Grid { get; set; } public PaginationModel pagination { get; set; } } public class AnnoModel : BaseModel { public string Pkey { get; set; } public string AnnoDate { get; set; } public string AnnoSubject { get; set; } public string AnnoContent { get; set; } public string AnnoStatus { get; set; } public string AnnoStatusName { get; set; } } public class AddSaveIn : BaseModel { [Required] public string AnnoDate { get; set; } [Required] public string AnnoSubject { get; set; } [Required] public string AnnoContent { get; set; } [Required] public string AnnoStatus { get; set; } } public class AddSaveOut : BaseModel { public string ResultMsg { get; set; } } public class EditSaveIn : BaseModel { [Required] public string Pkey { get; set; } [Required] public string AnnoDate { get; set; } [Required] public string AnnoSubject { get; set; } [Required] public string AnnoContent { get; set; } [Required] public string AnnoStatus { get; set; } } public class EditSaveOut : BaseModel { public string ResultMsg { get; set; } } public class DelCheckIn : BaseModel { public List<AnnoModel> checks { get; set; } } public class DelCheckOut : BaseModel { public string ResultMsg { get; set; } } } } |
修改 Controller 語法
前面做了這麼多工作,這裡要執行最後一個動作了,將原本在 AdmAnnoController 的語法簡化一下,改成執行 BusAnnouncement 裡面的方法就好了。
例如原本在查詢的語法有這麼多,而接下來就不用寫這麼多語法了,只要呼叫業務邏輯的方法就好。
在修改之前,我們需要先將 AdmAnnoController 繼承 Controller
改繼承 BaseController
。
接著將原本在 AdmAnnoController 的 4 個方法 Query()
, AddSave()
, EditSave()
, DelCheck()
,取代成以下的語法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
#region 查詢相關 /// <summary> /// 查詢公告 /// </summary> /// <param name="inModel"></param> /// <returns></returns> public IActionResult Query(QueryIn inModel) { QueryOut outModel = new QueryOut(); outModel.Grid = new List<AnnoModel>(); // 業務邏輯類別 BusAnnouncement bus = new BusAnnouncement(dbManager); // 分頁參數 bus.PageNo = inModel.pagination.pageNo; bus.PageSize = inModel.pagination.pageSize; // View 欄位 bus.AnnoSubject = inModel.AnnoSubject; bus.AnnoStatus = inModel.AnnoStatus; // 查詢結果 DataTable dt = bus.GetAnno(); // 將 DataRow 資料綁定到 Model 裡面 foreach (DataRow dr in dt.Rows) { AnnoModel gRow = new AnnoModel(); gRow = (AnnoModel)BindModelByDataRow(dr, dt.Columns, gRow); outModel.Grid.Add(gRow); } // 計算分頁 outModel.pagination = this.PreparePage(inModel.pagination, bus.TotalRowCount); return Json(outModel); } #endregion #region 新增相關 /// <summary> /// 新增公告 /// </summary> /// <param name="inModel"></param> /// <returns></returns> [ValidateAntiForgeryToken] public IActionResult AddSave(AddSaveIn inModel) { AddSaveOut outModel = new AddSaveOut(); // 檢查參數 if (ModelState.IsValid == false) { outModel.ErrMsg = string.Join("\n", ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage)); return Json(outModel); } // 業務邏輯類別 BusAnnouncement bus = new BusAnnouncement(dbManager); // View 欄位 bus.AnnoDate = inModel.AnnoDate; bus.AnnoSubject = inModel.AnnoSubject; bus.AnnoContent = inModel.AnnoContent; bus.AnnoStatus = inModel.AnnoStatus; // 執行新增 bus.InsertAnno(); outModel.ResultMsg = "新增完成"; return Json(outModel); } #endregion #region 修改相關 /// <summary> /// 修改公告 /// </summary> /// <param name="inModel"></param> /// <returns></returns> [ValidateAntiForgeryToken] public IActionResult EditSave(EditSaveIn inModel) { EditSaveOut outModel = new EditSaveOut(); // 檢查參數 if (ModelState.IsValid == false) { outModel.ErrMsg = string.Join("\n", ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage)); return Json(outModel); } // 業務邏輯類別 BusAnnouncement bus = new BusAnnouncement(dbManager); // View 欄位 bus.AnnoDate = inModel.AnnoDate; bus.AnnoSubject = inModel.AnnoSubject; bus.AnnoContent = inModel.AnnoContent; bus.AnnoStatus = inModel.AnnoStatus; bus.Pkey = inModel.Pkey; // 執行修改 int cnt = bus.UpdateAnno(); if (cnt > 0) { outModel.ResultMsg = "修改完成"; } else { outModel.ErrMsg = "未異動資料"; } return Json(outModel); } #endregion #region 刪除相關 /// <summary> /// 刪除公告 /// </summary> /// <param name="inModel"></param> /// <returns></returns> [ValidateAntiForgeryToken] public IActionResult DelCheck(DelCheckIn inModel) { DelCheckOut outModel = new DelCheckOut(); // 檢查參數 if (inModel.checks.Count == 0) { outModel.ErrMsg = "缺少輸入資料"; return Json(outModel); } // 業務邏輯類別 BusAnnouncement bus = new BusAnnouncement(dbManager); int ret = 0; foreach (AnnoModel model in inModel.checks) { bus.Pkey = model.Pkey; // 執行刪除 ret += bus.DeleteAnno(); } if (ret > 0) { outModel.ResultMsg = "成功刪除 " + ret + " 筆資料"; } return Json(outModel); } #endregion |
方法 PreparePage()
已經抽離至 BaseController 類別了,所以就不需要存在 AdmAnnoController 了。
寫到這裡你可以再按 F5 執行一下專案,是否操作結果跟原本的功能一樣,而 Controller 語法更簡潔了。
這都是因為前面做了這麼多底層工作,才可以讓業務邏輯的程式碼變的這麼短,將許多共用程式碼抽離至底層,讓 Controller 簡化語法,專注在解決業務問題,這也是物件導向的精神。
此篇文章屬於進階學習,是提昇底層架構的一種實作方法,如果你看的懂,恭喜你也擁有好的專案開發架構觀念。
範例下載
下一篇教學文章
相關學習文章
- [ASP.NET MVC] 前台會員註冊範例教學 #CH1 (附範例)
- [ASP.NET MVC] 免費公司形象網站範本套版教學 #CH1 (附範例)
- [ASP.NET MVC] 前台會員註冊範例教學 #CH1 (附範例)
如果你在學習上有不懂的地方,需要諮詢服務,可以參考站長服務,我想辨法解決你的問題
如果文章內容有過時、不適用或錯誤的地方,幫我在下方留言通知我一下,謝謝
哈哈
“%LIKE%”
我解決問題了
謝謝