您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關如何用MVC構架進行項目結構搭建,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
一、前言
下面將使用代碼的方式來一一解說各個層次。由于要搭建一個基本完整的結構,可能文章會比較長。另外出于實用的目的,因而并不會嚴格按照傳統的三層那樣進行非常明確的層次職能劃分。
二、需求說明
為方便大家理解,將以一個賬戶管理的小系統來進行解說,具體需求如下:
用戶信息分主要信息與擴展信息,一個用戶可以有(或沒有)一個用戶擴展信息。
記錄用戶的登錄記錄,一個用戶可以有多條登錄記錄,但登錄記錄所屬用戶唯一。
一個用戶可以有多個角色,一個角色也可以分配給多個用戶。
三、架構基礎
(一) 功能返回值
對于一個操作性業務功能(比如添加,修改,刪除),通常我們處理返回值的做法是使用簡單類型,通常會有如下幾種方案:
直接返回void,即什么也不返回,在操作過程中拋出異常,只要沒有異常拋出,就認為是操作成功了
返回是否操作成功的bool類型的返回值
返回操作變更后的新數據信息
返回表示各種結果的狀態碼的返回值
返回一個自定義枚舉來表示操作的各種結果
如果要返回多個值,還要使用 out 來添加返回參數
這樣做有什么不妥之處呢,我們來逐一分析:
靠拋異常的方式來終止系統的運行,異常是沿調用堆棧逐層向上拋出的,會造成很大的性能問題
bool值太死板,無法表示出業務操作中的各種情況
返回變更后的數據,還要與原始數據來判斷才能得到是否操作成功
用狀態碼解決了2的問題,但各種狀態碼的維護成本也會非常高
用枚舉值一定程序上解決了翻譯的問題,但還是要把枚舉值翻譯成各種情況的文字描述
綜上,我們到底需要一個怎樣的業務操作結果呢?
要能表示操作的成功失敗(廢話)
要能快速表示各種操作場景(如參數錯誤,查詢數據不存在,數據狀態不滿足操作要求等)
能返回附加的返回信息(如更新成功后有后續操作,需要使用更新后的新值)
最好在調用方能使用統一的代碼進行返回值處理
最好能自定義返回的文字描述信息
最好能把返回給用戶的信息與日志記錄的信息分開
再綜上,顯然簡單類型的返回值滿足不了需求了,那就需要定義一個專門用來封裝返回值信息的返回值類,這里定義如下:
/// <summary> /// 業務操作結果信息類,對操作結果進行封裝 /// </summary> public class OperationResult { #region 構造函數 /// <summary> /// 初始化一個 業務操作結果信息類 的新實例 /// </summary> /// <param name="resultType">業務操作結果類型</param> public OperationResult(OperationResultType resultType) { ResultType = resultType; } /// <summary> /// 初始化一個 定義返回消息的業務操作結果信息類 的新實例 /// </summary> /// <param name="resultType">業務操作結果類型</param> /// <param name="message">業務返回消息</param> public OperationResult(OperationResultType resultType, string message) : this(resultType) { Message = message; } /// <summary> /// 初始化一個 定義返回消息與附加數據的業務操作結果信息類 的新實例 /// </summary> /// <param name="resultType">業務操作結果類型</param> /// <param name="message">業務返回消息</param> /// <param name="appendData">業務返回數據</param> public OperationResult(OperationResultType resultType, string message, object appendData) : this(resultType, message) { AppendData = appendData; } /// <summary> /// 初始化一個 定義返回消息與日志消息的業務操作結果信息類 的新實例 /// </summary> /// <param name="resultType">業務操作結果類型</param> /// <param name="message">業務返回消息</param> /// <param name="logMessage">業務日志記錄消息</param> public OperationResult(OperationResultType resultType, string message, string logMessage) : this(resultType, message) { LogMessage = logMessage; } /// <summary> /// 初始化一個 定義返回消息、日志消息與附加數據的業務操作結果信息類 的新實例 /// </summary> /// <param name="resultType">業務操作結果類型</param> /// <param name="message">業務返回消息</param> /// <param name="logMessage">業務日志記錄消息</param> /// <param name="appendData">業務返回數據</param> public OperationResult(OperationResultType resultType, string message, string logMessage, object appendData) : this(resultType, message, logMessage) { AppendData = appendData; } #endregion #region 屬性 /// <summary> /// 獲取或設置 操作結果類型 /// </summary> public OperationResultType ResultType { get; set; } /// <summary> /// 獲取或設置 操作返回信息 /// </summary> public string Message { get; set; } /// <summary> /// 獲取或設置 操作返回的日志消息,用于記錄日志 /// </summary> public string LogMessage { get; set; } /// <summary> /// 獲取或設置 操作結果附加信息 /// </summary> public object AppendData { get; set; } #endregion }
再定義一個表示業務操作結果的枚舉,枚舉項上有一個DescriptionAttribute的特性,用來作為當上面的Message為空時的返回結果描述。
/// <summary> /// 表示業務操作結果的枚舉 /// </summary> [Description("業務操作結果的枚舉")] public enum OperationResultType { /// <summary> /// 操作成功 /// </summary> [Description("操作成功。")] Success, /// <summary> /// 操作取消或操作沒引發任何變化 /// </summary> [Description("操作沒有引發任何變化,提交取消。")] NoChanged, /// <summary> /// 參數錯誤 /// </summary> [Description("參數錯誤。")] ParamError, /// <summary> /// 指定參數的數據不存在 /// </summary> [Description("指定參數的數據不存在。")] QueryNull, /// <summary> /// 權限不足 /// </summary> [Description("當前用戶權限不足,不能繼續操作。")] PurviewLack, /// <summary> /// 非法操作 /// </summary> [Description("非法操作。")] IllegalOperation, /// <summary> /// 警告 /// </summary> [Description("警告")] Warning, /// <summary> /// 操作引發錯誤 /// </summary> [Description("操作引發錯誤。")] Error, }
(二) 實體基類
對于業務實體,有一些相同的且必要的信息,比如信息的創建時間,總是必要的;再比如想讓數據庫有一個“回收站”的功能,以給數據刪除做個緩沖,或者很多數據并非想從數據庫中徹底刪除掉,只是暫時的“禁用”一下,添加個邏輯刪除的標記也是必要的。再有就是想給所有實體數據倉儲操作來個類型限定,以防止傳入了其他非實體類型。基于以上理由,就有了下面這個實體基類:
/// <summary> /// 可持久到數據庫的領域模型的基類。 /// </summary> [Serializable] public abstract class Entity { #region 構造函數 /// <summary> /// 數據實體基類 /// </summary> protected Entity() { IsDeleted = false; AddDate = DateTime.Now; } #endregion #region 屬性 /// <summary> /// 獲取或設置 獲取或設置是否禁用,邏輯上的刪除,非物理刪除 /// </summary> public bool IsDeleted { get; set; } /// <summary> /// 獲取或設置 添加時間 /// </summary> [DataType(DataType.DateTime)] public DateTime AddDate { get; set; } /// <summary> /// 獲取或設置 版本控制標識,用于處理并發 /// </summary> [ConcurrencyCheck] 1621913756 public byte[] Timestamp { get; set; } #endregion }
這里要補充一下,本來實體基類中是可以定義一個表示“實體編號”的Id屬性的,但有個問題,如果定義了,就限定了Id屬性的數據類型了,但實際需求中可能有些實體使用自增的int類型,有些實體使用的是易于數據合并的guid類型,因此為靈活方便,不在此限制住 Id的數據類型。
四、架構分層
具體的架構分層如下圖所示:
(一) 核心業務層
根據 需求說明 中定義的需求,簡單起見,這里只實現一個簡單的用戶登錄功能:
用戶信息實體:
/// <summary> /// 實體類——用戶信息 /// </summary> [Description("用戶信息")] public class Member : Entity { /// <summary> /// 獲取或設置 用戶編號 /// </summary> public int Id { get; set; } /// <summary> /// 獲取或設置 用戶名 /// </summary> [Required] [StringLength(20)] public string UserName { get; set; } /// <summary> /// 獲取或設置 密碼 /// </summary> [Required] [StringLength(32)] public string Password { get; set; } /// <summary> /// 獲取或設置 用戶昵稱 /// </summary> [Required] [StringLength(20)] public string NickName { get; set; } /// <summary> /// 獲取或設置 用戶郵箱 /// </summary> [Required] [StringLength(50)] public string Email { get; set; } /// <summary> /// 獲取或設置 用戶擴展信息 /// </summary> public virtual MemberExtend Extend { get; set; } /// <summary> /// 獲取或設置 用戶擁有的角色信息集合 /// </summary> public virtual ICollection<Role> Roles { get; set; } /// <summary> /// 獲取或設置 用戶登錄記錄集合 /// </summary> public virtual ICollection<LoginLog> LoginLogs { get; set; } }
核心業務契約:注意接口的返回值使用了上面定義的返回值類
/// <summary> /// 賬戶模塊核心業務契約 /// </summary> public interface IAccountContract { /// <summary> /// 用戶登錄 /// </summary> /// <param name="loginInfo">登錄信息</param> /// <returns>業務操作結果</returns> OperationResult Login(LoginInfo loginInfo); }
核心業務實現:核心業務實現類為抽象類,因沒有數據訪問功能,這里使用了一個Members字段來充當數據源,業務功能的實現為虛方法,必要時可以在具體的客戶端(網站、桌面端,移動端)相應的派生類中進行重寫。請注意具體實現中對于返回值的處理。這里登錄只負責最核心的登錄業務操作,不涉及比如Http上下文狀態的操作。
/// <summary> /// 賬戶模塊核心業務實現 /// </summary> public abstract class AccountService : IAccountContract { private static readonly Member[] Members = new[] { new Member { UserName = "admin", Password = "123456", Email = "admin@gmfcn.net", NickName = "管理員" }, new Member { UserName = "gmfcn", Password = "123456", Email = "mf.guo@qq.com", NickName = "郭明鋒" } }; private static readonly List<LoginLog> LoginLogs = new List<LoginLog>(); /// <summary> /// 用戶登錄 /// </summary> /// <param name="loginInfo">登錄信息</param> /// <returns>業務操作結果</returns> public virtual OperationResult Login(LoginInfo loginInfo) { PublicHelper.CheckArgument(loginInfo, "loginInfo"); Member member = Members.SingleOrDefault(m => m.UserName == loginInfo.Access || m.Email == loginInfo.Access); if (member == null) { return new OperationResult(OperationResultType.QueryNull, "指定賬號的用戶不存在。"); } if (member.Password != loginInfo.Password) { return new OperationResult(OperationResultType.Warning, "登錄密碼不正確。"); } LoginLog loginLog = new LoginLog { IpAddress = loginInfo.IpAddress, Member = member }; LoginLogs.Add(loginLog); return new OperationResult(OperationResultType.Success, "登錄成功。", member); } }
(二) 站點業務層
站點業務契約:站點業務契約繼承核心業務契約,即可擁有核心層定義的業務功能。站點登錄驗證使用了Forms的Cookie驗證,這里的退出不涉及核心層的操作,因而核心層沒有退出功能
/// <summary> /// 賬戶模塊站點業務契約 /// </summary> public interface IAccountSiteContract : IAccountContract { /// <summary> /// 用戶登錄 /// </summary> /// <param name="model">登錄模型信息</param> /// <returns>業務操作結果</returns> OperationResult Login(LoginModel model); /// <summary> /// 用戶退出 /// </summary> void Logout(); }
站點業務實現:站點業務實現繼承核心業務實現與站點業務契約,負責把從UI中接收到的視圖模型信息轉換為符合核心層定義的參數,并處理與網站狀態相關的Session,Cookie等Http相關業務
/// <summary> /// 賬戶模塊站點業務實現 /// </summary> public class AccountSiteService : AccountService, IAccountSiteContract { /// <summary> /// 用戶登錄 /// </summary> /// <param name="model">登錄模型信息</param> /// <returns>業務操作結果</returns> public OperationResult Login(LoginModel model) { PublicHelper.CheckArgument(model, "model"); LoginInfo loginInfo = new LoginInfo { Access = model.Account, Password = model.Password, IpAddress = HttpContext.Current.Request.UserHostAddress }; OperationResult result = base.Login(loginInfo); if (result.ResultType == OperationResultType.Success) { Member member = (Member)result.AppendData; DateTime expiration = model.IsRememberLogin ? DateTime.Now.AddDays(7) : DateTime.Now.Add(FormsAuthentication.Timeout); FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, member.UserName, DateTime.Now, expiration, true, member.NickName, FormsAuthentication.FormsCookiePath); HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket)); if (model.IsRememberLogin) { cookie.Expires = DateTime.Now.AddDays(7); } HttpContext.Current.Response.Cookies.Set(cookie); result.AppendData = null; } return result; } /// <summary> /// 用戶退出 /// </summary> public void Logout() { FormsAuthentication.SignOut(); } }
(三) 站點展現層
MVC控制器:Action提供統一風格的代碼來對業務操作結果OperationResult進行處理
public class AccountController : Controller { public AccountController() { AccountContract = new AccountSiteService(); } #region 屬性 public IAccountSiteContract AccountContract { get; set; } #endregion #region 視圖功能 public ActionResult Login() { string returnUrl = Request.Params["returnUrl"]; returnUrl = returnUrl ?? Url.Action("Index", "Home", new { area = "" }); LoginModel model = new LoginModel { ReturnUrl = returnUrl }; return View(model); } [HttpPost] public ActionResult Login(LoginModel model) { try { OperationResult result = AccountContract.Login(model); string msg = result.Message ?? result.ResultType.ToDescription(); if (result.ResultType == OperationResultType.Success) { return Redirect(model.ReturnUrl); } ModelState.AddModelError("", msg); return View(model); } catch (Exception e) { ModelState.AddModelError("", e.Message); return View(model); } } public ActionResult Logout( ) { string returnUrl = Request.Params["returnUrl"]; returnUrl = returnUrl ?? Url.Action("Index", "Home", new { area = "" }); if (User.Identity.IsAuthenticated) { AccountContract.Logout(); } return Redirect(returnUrl); } #endregion }
MVC 視圖:
@model GMF.Demo.Site.Models.LoginModel @{ ViewBag.Title = "Login"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h3>Login</h3> @using (Html.BeginForm()) { @Html.AntiForgeryToken() @Html.ValidationSummary(true) <fieldset> <legend>LoginModel</legend> <div class="editor-label"> @Html.LabelFor(model => model.Account) </div> <div class="editor-field"> @Html.EditorFor(model => model.Account) @Html.ValidationMessageFor(model => model.Account) </div> <div class="editor-label"> @Html.LabelFor(model => model.Password) </div> <div class="editor-field"> @Html.EditorFor(model => model.Password) @Html.ValidationMessageFor(model => model.Password) </div> <div class="editor-label"> @Html.LabelFor(model => model.IsRememberLogin) </div> <div class="editor-field"> @Html.EditorFor(model => model.IsRememberLogin) @Html.ValidationMessageFor(model => model.IsRememberLogin) </div> @Html.HiddenFor(m => m.ReturnUrl) <p> <input type="submit" value="登錄" /> </p> </fieldset> } <div> @Html.ActionLink("Back to List", "Index", "Home") </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }
至此,整個項目構架搭建完成,運行結果如下:
以上就是如何用MVC構架進行項目結構搭建,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。