溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何用MVC構(gòu)架進(jìn)行項(xiàng)目結(jié)構(gòu)搭建

發(fā)布時(shí)間:2021-12-13 09:52:40 來(lái)源:億速云 閱讀:149 作者:柒染 欄目:開(kāi)發(fā)技術(shù)

本篇文章給大家分享的是有關(guān)如何用MVC構(gòu)架進(jìn)行項(xiàng)目結(jié)構(gòu)搭建,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧。

一、前言

下面將使用代碼的方式來(lái)一一解說(shuō)各個(gè)層次。由于要搭建一個(gè)基本完整的結(jié)構(gòu),可能文章會(huì)比較長(zhǎng)。另外出于實(shí)用的目的,因而并不會(huì)嚴(yán)格按照傳統(tǒng)的三層那樣進(jìn)行非常明確的層次職能劃分。

二、需求說(shuō)明

為方便大家理解,將以一個(gè)賬戶管理的小系統(tǒng)來(lái)進(jìn)行解說(shuō),具體需求如下:

  1. 用戶信息分主要信息與擴(kuò)展信息,一個(gè)用戶可以有(或沒(méi)有)一個(gè)用戶擴(kuò)展信息。

  2. 記錄用戶的登錄記錄,一個(gè)用戶可以有多條登錄記錄,但登錄記錄所屬用戶唯一。

  3. 一個(gè)用戶可以有多個(gè)角色,一個(gè)角色也可以分配給多個(gè)用戶。

三、架構(gòu)基礎(chǔ)

(一) 功能返回值

對(duì)于一個(gè)操作性業(yè)務(wù)功能(比如添加,修改,刪除),通常我們處理返回值的做法是使用簡(jiǎn)單類(lèi)型,通常會(huì)有如下幾種方案:

  1. 直接返回void,即什么也不返回,在操作過(guò)程中拋出異常,只要沒(méi)有異常拋出,就認(rèn)為是操作成功了

  2. 返回是否操作成功的bool類(lèi)型的返回值

  3. 返回操作變更后的新數(shù)據(jù)信息

  4. 返回表示各種結(jié)果的狀態(tài)碼的返回值

  5. 返回一個(gè)自定義枚舉來(lái)表示操作的各種結(jié)果

  6. 如果要返回多個(gè)值,還要使用 out 來(lái)添加返回參數(shù)

這樣做有什么不妥之處呢,我們來(lái)逐一分析:

  1. 靠拋異常的方式來(lái)終止系統(tǒng)的運(yùn)行,異常是沿調(diào)用堆棧逐層向上拋出的,會(huì)造成很大的性能問(wèn)題

  2. bool值太死板,無(wú)法表示出業(yè)務(wù)操作中的各種情況

  3. 返回變更后的數(shù)據(jù),還要與原始數(shù)據(jù)來(lái)判斷才能得到是否操作成功

  4. 用狀態(tài)碼解決了2的問(wèn)題,但各種狀態(tài)碼的維護(hù)成本也會(huì)非常高

  5. 用枚舉值一定程序上解決了翻譯的問(wèn)題,但還是要把枚舉值翻譯成各種情況的文字描述

綜上,我們到底需要一個(gè)怎樣的業(yè)務(wù)操作結(jié)果呢?

  1. 要能表示操作的成功失?。◤U話)

  2. 要能快速表示各種操作場(chǎng)景(如參數(shù)錯(cuò)誤,查詢(xún)數(shù)據(jù)不存在,數(shù)據(jù)狀態(tài)不滿足操作要求等)

  3. 能返回附加的返回信息(如更新成功后有后續(xù)操作,需要使用更新后的新值)

  4. 最好在調(diào)用方能使用統(tǒng)一的代碼進(jìn)行返回值處理

  5. 最好能自定義返回的文字描述信息

  6. 最好能把返回給用戶的信息與日志記錄的信息分開(kāi)

再綜上,顯然簡(jiǎn)單類(lèi)型的返回值滿足不了需求了,那就需要定義一個(gè)專(zhuān)門(mén)用來(lái)封裝返回值信息的返回值類(lèi),這里定義如下:

/// <summary>       ///     業(yè)務(wù)操作結(jié)果信息類(lèi),對(duì)操作結(jié)果進(jìn)行封裝       /// </summary>       public class OperationResult       {           #region 構(gòu)造函數(shù)              /// <summary>           ///     初始化一個(gè) 業(yè)務(wù)操作結(jié)果信息類(lèi) 的新實(shí)例           /// </summary>           /// <param name="resultType">業(yè)務(wù)操作結(jié)果類(lèi)型</param>           public OperationResult(OperationResultType resultType)           {               ResultType = resultType;           }              /// <summary>           ///     初始化一個(gè) 定義返回消息的業(yè)務(wù)操作結(jié)果信息類(lèi) 的新實(shí)例           /// </summary>           /// <param name="resultType">業(yè)務(wù)操作結(jié)果類(lèi)型</param>           /// <param name="message">業(yè)務(wù)返回消息</param>           public OperationResult(OperationResultType resultType, string message)               : this(resultType)           {               Message = message;           }              /// <summary>           ///     初始化一個(gè) 定義返回消息與附加數(shù)據(jù)的業(yè)務(wù)操作結(jié)果信息類(lèi) 的新實(shí)例           /// </summary>           /// <param name="resultType">業(yè)務(wù)操作結(jié)果類(lèi)型</param>           /// <param name="message">業(yè)務(wù)返回消息</param>           /// <param name="appendData">業(yè)務(wù)返回?cái)?shù)據(jù)</param>           public OperationResult(OperationResultType resultType, string message, object appendData)               : this(resultType, message)           {               AppendData = appendData;           }              /// <summary>           ///     初始化一個(gè) 定義返回消息與日志消息的業(yè)務(wù)操作結(jié)果信息類(lèi) 的新實(shí)例           /// </summary>           /// <param name="resultType">業(yè)務(wù)操作結(jié)果類(lèi)型</param>           /// <param name="message">業(yè)務(wù)返回消息</param>           /// <param name="logMessage">業(yè)務(wù)日志記錄消息</param>           public OperationResult(OperationResultType resultType, string message, string logMessage)               : this(resultType, message)           {               LogMessage = logMessage;           }              /// <summary>           ///     初始化一個(gè) 定義返回消息、日志消息與附加數(shù)據(jù)的業(yè)務(wù)操作結(jié)果信息類(lèi) 的新實(shí)例           /// </summary>           /// <param name="resultType">業(yè)務(wù)操作結(jié)果類(lèi)型</param>           /// <param name="message">業(yè)務(wù)返回消息</param>           /// <param name="logMessage">業(yè)務(wù)日志記錄消息</param>           /// <param name="appendData">業(yè)務(wù)返回?cái)?shù)據(jù)</param>           public OperationResult(OperationResultType resultType, string message, string logMessage, object appendData)               : this(resultType, message, logMessage)           {               AppendData = appendData;           }              #endregion              #region 屬性              /// <summary>           ///     獲取或設(shè)置 操作結(jié)果類(lèi)型           /// </summary>           public OperationResultType ResultType { get; set; }              /// <summary>           ///     獲取或設(shè)置 操作返回信息           /// </summary>           public string Message { get; set; }              /// <summary>           ///     獲取或設(shè)置 操作返回的日志消息,用于記錄日志           /// </summary>           public string LogMessage { get; set; }              /// <summary>           ///     獲取或設(shè)置 操作結(jié)果附加信息           /// </summary>           public object AppendData { get; set; }              #endregion       }

再定義一個(gè)表示業(yè)務(wù)操作結(jié)果的枚舉,枚舉項(xiàng)上有一個(gè)DescriptionAttribute的特性,用來(lái)作為當(dāng)上面的Message為空時(shí)的返回結(jié)果描述。

/// <summary>       ///     表示業(yè)務(wù)操作結(jié)果的枚舉       /// </summary>       [Description("業(yè)務(wù)操作結(jié)果的枚舉")]       public enum OperationResultType       {           /// <summary>           ///     操作成功           /// </summary>           [Description("操作成功。")]           Success,              /// <summary>           ///     操作取消或操作沒(méi)引發(fā)任何變化           /// </summary>           [Description("操作沒(méi)有引發(fā)任何變化,提交取消。")]           NoChanged,              /// <summary>           ///     參數(shù)錯(cuò)誤           /// </summary>           [Description("參數(shù)錯(cuò)誤。")]           ParamError,              /// <summary>           ///     指定參數(shù)的數(shù)據(jù)不存在           /// </summary>           [Description("指定參數(shù)的數(shù)據(jù)不存在。")]           QueryNull,              /// <summary>           ///     權(quán)限不足           /// </summary>           [Description("當(dāng)前用戶權(quán)限不足,不能繼續(xù)操作。")]           PurviewLack,              /// <summary>           ///     非法操作           /// </summary>           [Description("非法操作。")]           IllegalOperation,              /// <summary>           ///     警告           /// </summary>           [Description("警告")]           Warning,              /// <summary>           ///     操作引發(fā)錯(cuò)誤           /// </summary>           [Description("操作引發(fā)錯(cuò)誤。")]           Error,       }

(二) 實(shí)體基類(lèi)

對(duì)于業(yè)務(wù)實(shí)體,有一些相同的且必要的信息,比如信息的創(chuàng)建時(shí)間,總是必要的;再比如想讓數(shù)據(jù)庫(kù)有一個(gè)“回收站”的功能,以給數(shù)據(jù)刪除做個(gè)緩沖,或者很多數(shù)據(jù)并非想從數(shù)據(jù)庫(kù)中徹底刪除掉,只是暫時(shí)的“禁用”一下,添加個(gè)邏輯刪除的標(biāo)記也是必要的。再有就是想給所有實(shí)體數(shù)據(jù)倉(cāng)儲(chǔ)操作來(lái)個(gè)類(lèi)型限定,以防止傳入了其他非實(shí)體類(lèi)型?;谝陨侠碛桑陀辛讼旅孢@個(gè)實(shí)體基類(lèi):

/// <summary>       ///     可持久到數(shù)據(jù)庫(kù)的領(lǐng)域模型的基類(lèi)。       /// </summary>       [Serializable]       public abstract class Entity       {           #region 構(gòu)造函數(shù)              /// <summary>           ///     數(shù)據(jù)實(shí)體基類(lèi)           /// </summary>           protected Entity()           {               IsDeleted = false;               AddDate = DateTime.Now;           }              #endregion              #region 屬性              /// <summary>           ///     獲取或設(shè)置 獲取或設(shè)置是否禁用,邏輯上的刪除,非物理刪除           /// </summary>           public bool IsDeleted { get; set; }              /// <summary>           ///     獲取或設(shè)置 添加時(shí)間           /// </summary>           [DataType(DataType.DateTime)]           public DateTime AddDate { get; set; }              /// <summary>           ///     獲取或設(shè)置 版本控制標(biāo)識(shí),用于處理并發(fā)           /// </summary>           [ConcurrencyCheck]           1621913756           public byte[] Timestamp { get; set; }              #endregion       }

這里要補(bǔ)充一下,本來(lái)實(shí)體基類(lèi)中是可以定義一個(gè)表示“實(shí)體編號(hào)”的Id屬性的,但有個(gè)問(wèn)題,如果定義了,就限定了Id屬性的數(shù)據(jù)類(lèi)型了,但實(shí)際需求中可能有些實(shí)體使用自增的int類(lèi)型,有些實(shí)體使用的是易于數(shù)據(jù)合并的guid類(lèi)型,因此為靈活方便,不在此限制住 Id的數(shù)據(jù)類(lèi)型。

四、架構(gòu)分層

具體的架構(gòu)分層如下圖所示:

如何用MVC構(gòu)架進(jìn)行項(xiàng)目結(jié)構(gòu)搭建

(一) 核心業(yè)務(wù)層

根據(jù) 需求說(shuō)明 中定義的需求,簡(jiǎn)單起見(jiàn),這里只實(shí)現(xiàn)一個(gè)簡(jiǎn)單的用戶登錄功能:

用戶信息實(shí)體:

/// <summary>       ///     實(shí)體類(lèi)&mdash;&mdash;用戶信息       /// </summary>       [Description("用戶信息")]       public class Member : Entity       {           /// <summary>           /// 獲取或設(shè)置 用戶編號(hào)           /// </summary>           public int Id { get; set; }              /// <summary>           /// 獲取或設(shè)置 用戶名           /// </summary>           [Required]           [StringLength(20)]           public string UserName { get; set; }              /// <summary>           /// 獲取或設(shè)置 密碼           /// </summary>           [Required]           [StringLength(32)]           public string Password { get; set; }              /// <summary>           /// 獲取或設(shè)置 用戶昵稱(chēng)           /// </summary>           [Required]           [StringLength(20)]           public string NickName { get; set; }              /// <summary>           /// 獲取或設(shè)置 用戶郵箱           /// </summary>           [Required]           [StringLength(50)]           public string Email { get; set; }              /// <summary>           /// 獲取或設(shè)置 用戶擴(kuò)展信息           /// </summary>           public virtual MemberExtend Extend { get; set; }              /// <summary>           /// 獲取或設(shè)置 用戶擁有的角色信息集合           /// </summary>           public virtual ICollection<Role> Roles { get; set; }              /// <summary>           /// 獲取或設(shè)置 用戶登錄記錄集合           /// </summary>           public virtual ICollection<LoginLog> LoginLogs { get; set; }       }

核心業(yè)務(wù)契約:注意接口的返回值使用了上面定義的返回值類(lèi)

/// <summary>       ///     賬戶模塊核心業(yè)務(wù)契約       /// </summary>       public interface IAccountContract       {           /// <summary>           /// 用戶登錄           /// </summary>           /// <param name="loginInfo">登錄信息</param>           /// <returns>業(yè)務(wù)操作結(jié)果</returns>           OperationResult Login(LoginInfo loginInfo);       }

核心業(yè)務(wù)實(shí)現(xiàn):核心業(yè)務(wù)實(shí)現(xiàn)類(lèi)為抽象類(lèi),因沒(méi)有數(shù)據(jù)訪問(wèn)功能,這里使用了一個(gè)Members字段來(lái)充當(dāng)數(shù)據(jù)源,業(yè)務(wù)功能的實(shí)現(xiàn)為虛方法,必要時(shí)可以在具體的客戶端(網(wǎng)站、桌面端,移動(dòng)端)相應(yīng)的派生類(lèi)中進(jìn)行重寫(xiě)。請(qǐng)注意具體實(shí)現(xiàn)中對(duì)于返回值的處理。這里登錄只負(fù)責(zé)最核心的登錄業(yè)務(wù)操作,不涉及比如Http上下文狀態(tài)的操作。

/// <summary>       ///     賬戶模塊核心業(yè)務(wù)實(shí)現(xiàn)       /// </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>業(yè)務(wù)操作結(jié)果</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, "指定賬號(hào)的用戶不存在。");               }               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);           }       }

(二) 站點(diǎn)業(yè)務(wù)層

站點(diǎn)業(yè)務(wù)契約:站點(diǎn)業(yè)務(wù)契約繼承核心業(yè)務(wù)契約,即可擁有核心層定義的業(yè)務(wù)功能。站點(diǎn)登錄驗(yàn)證使用了Forms的Cookie驗(yàn)證,這里的退出不涉及核心層的操作,因而核心層沒(méi)有退出功能

/// <summary>       ///     賬戶模塊站點(diǎn)業(yè)務(wù)契約       /// </summary>       public interface IAccountSiteContract : IAccountContract       {           /// <summary>           ///     用戶登錄           /// </summary>           /// <param name="model">登錄模型信息</param>           /// <returns>業(yè)務(wù)操作結(jié)果</returns>           OperationResult Login(LoginModel model);              /// <summary>           ///     用戶退出           /// </summary>           void Logout();       }

站點(diǎn)業(yè)務(wù)實(shí)現(xiàn):站點(diǎn)業(yè)務(wù)實(shí)現(xiàn)繼承核心業(yè)務(wù)實(shí)現(xiàn)與站點(diǎn)業(yè)務(wù)契約,負(fù)責(zé)把從UI中接收到的視圖模型信息轉(zhuǎn)換為符合核心層定義的參數(shù),并處理與網(wǎng)站狀態(tài)相關(guān)的Session,Cookie等Http相關(guān)業(yè)務(wù)

/// <summary>       ///     賬戶模塊站點(diǎn)業(yè)務(wù)實(shí)現(xiàn)       /// </summary>       public class AccountSiteService : AccountService, IAccountSiteContract       {           /// <summary>           ///     用戶登錄           /// </summary>           /// <param name="model">登錄模型信息</param>           /// <returns>業(yè)務(wù)操作結(jié)果</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();           }       }

(三) 站點(diǎn)展現(xiàn)層

MVC控制器:Action提供統(tǒng)一風(fēng)格的代碼來(lái)對(duì)業(yè)務(wù)操作結(jié)果OperationResult進(jìn)行處理

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")  }

至此,整個(gè)項(xiàng)目構(gòu)架搭建完成,運(yùn)行結(jié)果如下:
如何用MVC構(gòu)架進(jìn)行項(xiàng)目結(jié)構(gòu)搭建 如何用MVC構(gòu)架進(jìn)行項(xiàng)目結(jié)構(gòu)搭建

以上就是如何用MVC構(gòu)架進(jìn)行項(xiàng)目結(jié)構(gòu)搭建,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見(jiàn)到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

mvc
AI