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


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

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

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、完整項目架構如下

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM