天天看點

項目架構開發:展現層(上)

上次我們建立了項目的服務層,服務層在業務邏輯簡單,或項目運作初期不是很容易展現出他的價值;傳送門:項目架構開發:服務層(上)

服務層專門處理非業務邏輯的一些功能,比如緩存、異常處理、組織多個應用邏輯等;這次我們搭建最上層的展現層,用到的知識面包括以下:

asp.net mvc5 + bootstrap + autofac + AutoMapper

這次我們沒有用服務層,而是直接調用應用邏輯層接口方法,其實對小項目來說,這樣已經足夠了;服務層我們下次再講吧

現在開始吧!

1、建立MVC + UnitTest

項目架構開發:展現層(上)

先搭建個架構,網上找的背景模闆

項目架構開發:展現層(上)
項目架構開發:展現層(上)

 2、ViewModel

 UI的資料載體最好建立一個viewmodel,這樣就不用依賴DTO或PO,因為頁面上顯示的資料實體一般比較大,會封裝比DTO多的多的屬性

項目架構開發:展現層(上)

LoginUserViewModel.cs

1 using Infrastructure.Common;      2 using System;      3 using System.Collections.Generic;      4       5 namespace Presentation.MVC.Models      6 {      7     public class LoginUserViewModel      8     {      9         public int RowNumber { get; set; }     10      11         public Guid Id { get; set; }     12         public string LoginName { get; set; }     13         public short? IsEnabled { get; set; }     14         public DateTime? CreateTime { get; set; }     15     }     16      17     public class LoginUserListViewModel     18     {     19         public List<LoginUserViewModel> Items { get; set; }     20     }     21      22     public class LoginUserPageViewModel : PageViewModelBase     23     {     24         public List<LoginUserViewModel> Items { get; set; }     25     }     26 }      

PageViewModelBase.cs (這個是分頁時候用的,如上邊标紅實體)

1 using Infrastructure.Common;      2       3 namespace Presentation.MVC.Models      4 {      5     public class PageViewModelBase      6     {      7         public bool IsFirst { get; set; }      8         public bool IsLast { get; set; }      9      10         public Page Page { get; set; }     11         public int Total { get; set; }     12         public int TotalPage     13         {     14             get     15             {     16                 return (Total % Page.PageSize) == 0 ? Total / Page.PageSize : (Total / Page.PageSize) + 1;     17             }     18         }     19      20         public int PrePage     21         {     22             get     23             {     24                 if (Page.PageIndex == 1)     25                 {     26                     IsFirst = true;     27                     return 1;     28                 }     29                 else     30                 {     31                     IsFirst = false;     32                     return Page.PageIndex - 1;     33                 }     34             }     35         }     36         public int NextPage     37         {     38             get     39             {     40                 if (Page.PageIndex == TotalPage)     41                 {     42                     IsLast = true;     43                     return TotalPage;     44                 }     45                 else     46                 {     47                     IsLast = false;     48                     return Page.PageIndex + 1;     49                 }     50             }     51         }     52     }     53 }      

 3、映射

 主要是DTO映射成ViewModel,這裡我們用的是AutoMapper

項目架構開發:展現層(上)

AutoMapperConfiguration.cs,AutoMapper用法很簡單,引用他,然後像下邊代碼那樣寫,然後再應用啟動的時候加載

1 using AutoMapper;      2 using Business.ReverseDDD.Model;      3 using Presentation.MVC.Models;      4       5 namespace Presentation.MVC.Mappings      6 {      7     public class AutoMapperConfiguration      8     {      9         public static void Configure()     10         {     11             Mapper.CreateMap<LoginUser, LoginUserViewModel>();     12     13         }     14     }     15 }      

Global.asax.cs

1 protected void Application_Start()     2         {     3             AreaRegistration.RegisterAllAreas();     4             FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);     5             RouteConfig.RegisterRoutes(RouteTable.Routes);     6             BundleConfig.RegisterBundles(BundleTable.Bundles);     7      8             AutoMapperConfiguration.Configure();     9         }      

4、視圖

項目架構開發:展現層(上)

為了友善,CURD都在一個頁面實作了

1 @model Presentation.MVC.Models.LoginUserPageViewModel       2 @{       3     ViewBag.Title = "Index";       4     Layout = "~/Views/Shared/_Bootstrap.cshtml";       5 }       6        7 <div class="container">       8     <h2></h2>       9     <div class="panel panel-default">      10         <div class="panel-heading">      11             <h4 class="panel-title">      12                 <a data-toggle="collapse" data-parent="#accordion"      13                    href="#collapseOne" id="actionType">      14                     新增使用者      15                 </a>      16             </h4>      17         </div>      18         <div id="collapseOne" class="panel-collapse collapse">      19             <div class="panel-body">      20       21                 @using (Html.BeginForm("Add", "LoginUser", null, FormMethod.Post, new { @id = "formLoginUser", @class = "form-horizontal", role = "form" }))      22                 {      23                     @Html.AntiForgeryToken()      24       25                     <div class="form-group">      26                         <label for="firstname" class="col-sm-2 control-label">登入名</label>      27                         <div class="col-sm-10">      28                             <input type="text" class="form-control" name="LoginName" id="LoginName"      29                                    placeholder="請輸入登入賬戶名" />      30                         </div>      31                     </div>      32                     <div class="form-group">      33                         <label for="lastname" class="col-sm-2 control-label">登入密碼</label>      34                         <div class="col-sm-10">      35                             <input type="text" class="form-control" name="Password" id="Password"      36                                    placeholder="請輸入登入密碼" />      37                         </div>      38                     </div>      39                     <div class="form-group">      40                         <div class="col-sm-offset-2 col-sm-10">      41                             <div class="checkbox">      42                                 <label>      43                                     <input type="checkbox" name="IsEnabled" id="IsEnabled" value="1" /> 是否有效      44                                 </label>      45                             </div>      46                         </div>      47                     </div>      48       49                     <div class="form-group">      50                         <div class="col-sm-offset-2 col-sm-10">      51                             <input type="hidden" name="Id" id="Id" />      52                             <button type="submit" class="btn btn-default">送出</button>      53                         </div>      54                     </div>      55                 }      56       57             </div>      58         </div>      59     </div>      60       61       62     <h4>使用者清單</h4>      63     <table id="list" class="table table-striped table-bordered table-hover table-condensed">      64         <thead>      65             <tr>      66                 <th>序号</th>      67                 <th>登入名</th>      68                 <th>是否有效</th>      69                 <th>建立時間</th>      70                 <th>操作</th>      71             </tr>      72         </thead>      73         <tbody>      74             @foreach (var item in Model.Items)      75             {      76                 <tr>      77                     <td>@item.RowNumber</td>      78                     <td>@item.LoginName</td>      79                     <td>@item.IsEnabled</td>      80                     <td>@item.CreateTime</td>      81                     <td>      82                         <button type="button" class="btn btn-link btn-xs" key="@item.Id" action="edit">修改</button>      83                         <button type="button" class="btn btn-link btn-xs" key="@item.Id" action="delete">删除</button>      84                     </td>      85                 </tr>      86             }      87         </tbody>      88     </table>      89       90     <ul class="pagination">      91         <li><a href="/LoginUsers/@Model.PrePage">&laquo;</a></li>      92         @for (int index = 1; index <= Model.TotalPage; index++)      93         {      94             if (Model.Page.PageIndex == index)      95             {      96                 <li class="active"><a href="/LoginUsers/@index">@index</a></li>      97             }      98             else      99             {     100                 <li><a href="/LoginUsers/@index">@index</a></li>     101             }     102         }     103         <li><a href="/LoginUsers/@Model.NextPage">&raquo;</a></li>     104     </ul>     105      106     <script type="text/javascript">     107         $(function () {     108             $(".btn-link").click(function () {     109                 var obj = $(this);     110                 var key = obj.attr("key");     111                 if (key == undefined || key == null || key == "") return;     112      113                 var action = obj.attr("action");     114      115                 if (action == "delete") {     116                     CreateDeleteWindow(function () {     117                         location.href = "/LoginUser/Delete/" + key;     118                     });     119                 }     120      121                 if (action == "edit") {     122                     $.ajax({     123                         url: "/LoginUser/Edit/" + key,     124                         type: "GET",     125                         dataType: 'json',     126                         success: function (result) {     127                             $("#Id").val(result.Id);     128                             $("#LoginName").val(result.LoginName);     129                             if (result.IsEnabled == 1) $("#IsEnabled").attr("checked", "true");     130                             else $("#IsEnabled").removeAttr("checked");     131      132                             $('#collapseOne').collapse('show');     133                             $("#formLoginUser").attr("action", "/LoginUser/Edit");     134                             $("#actionType").html("修改使用者");     135                         },     136                         error: function (e) {     137                             alert(e);     138                         }     139                     });     140                 }     141             });     142      143         });     144     </script>     145      146 </div>      

其實也沒什麼特别的就是用了Bootstrap美化頁面樣式,對Bootstrap不懂的請點選這裡

5、控制器

LoginUserController.cs

1 using AutoMapper;      2 using Business.DTO.Request;      3 using Business.ReverseDDD.IApplication;      4 using Infrastructure.Common;      5 using LingExtensions;      6 using Presentation.MVC.Models;      7 using System;      8 using System.Collections.Generic;      9 using System.Web.Mvc;     10      11 namespace Presentation.MVC.Controllers     12 {     13     public class LoginUserController : Controller     14     {     15         private ILoginUserApplication loginUserApplication;     16      17         public LoginUserController(ILoginUserApplication loginUserApplication)     18         {     19             this.loginUserApplication = loginUserApplication;     20         }     21      22         [Route("LoginUsers/{PageIndex=1}")]     23         public ActionResult Index(string PageIndex)     24         {     25             Page page = new Page();     26             page.PageIndex = PageIndex.ToInt(1);     27      28             var model = new LoginUserPageViewModel();     29             var soure = this.loginUserApplication.GetPage(page, w => w.OrderByDescending(t => t.CreateTime));     30      31             model.Items = Mapper.Map<List<LoginUserViewModel>>(soure.Item2);     32             model.Total = soure.Item1;     33             model.Page = page;     34      35             return View(model);     36         }     37      38         [HttpPost]     39         [ValidateAntiForgeryToken]      40         public ActionResult Add(LoginUserCURequest entity)     41         {     42             this.loginUserApplication.Add(new LoginUserCURequest()     43             {     44                 Id = Guid.NewGuid(),     45                 LoginName = entity.LoginName,     46                 Password = entity.Password,     47                 IsEnabled = entity.IsEnabled     48             });     49      50             return RedirectToAction("Index");     51         }     52      53         [HttpGet]     54         public ActionResult Delete(string id)     55         {     56             this.loginUserApplication.Delete(Guid.Parse(id));     57      58             return RedirectToAction("Index");     59         }     60      61         [HttpGet]     62         public ActionResult Edit(Guid id)     63         {     64             var soure = this.loginUserApplication.Get(id);     65      66             return Json(soure, JsonRequestBehavior.AllowGet);     67         }     68      69         [HttpPost]     70         [ValidateAntiForgeryToken]     71         public ActionResult Edit(LoginUserCURequest entity)     72         {     73             this.loginUserApplication.Update(new LoginUserCURequest()     74             {     75                 Id = entity.Id,     76                 LoginName = entity.LoginName,     77                 Password = entity.Password,     78                 IsEnabled = entity.IsEnabled     79             });     80      81             return RedirectToAction("Index");     82         }     83     }     84 }      

都是示範CURD的功能,大家不要在意這些細節。。

看标紅的地方,意思是将soure.Item2(是Tuple<int, IEnumerable<LoginUser>>類型)轉換成List<LoginUserViewModel>

這就是AutoMapper的用法

還有是這裡沒有依賴具體應用邏輯元件的,隻依賴了業務邏輯接口using Business.ReverseDDD.IApplication;

這個是為了解耦,而且對分層并行開發很有用,項目前端後端開發都不用依賴誰開發完才能往下繼續;

控制器我們用的是依賴注入Autofac元件:

項目架構開發:展現層(上)
項目架構開發:展現層(上)

 6、UnitTest

項目架構開發:展現層(上)

LoginUserControllerTest.cs, 記得也要Mapping哦

項目架構開發:展現層(上)
項目架構開發:展現層(上)

測試通過了

7、UI,我們來看看界面功能

 7.1 新增使用者

項目架構開發:展現層(上)

使用者新增成功,清單正常顯示資料

項目架構開發:展現層(上)

7.2 修改資料

項目架構開發:展現層(上)

修改成功

項目架構開發:展現層(上)

7.3 分頁正常

項目架構開發:展現層(上)

至此,展現層完成了

8、完整項目架構如下

項目架構開發:展現層(上)

繼續閱讀