C#進階系列——DDD領域驅動設計初探(七):Web層的搭建
前言:好久沒更新部落格了,每天被該死的業務纏身,今天正好一個模組完成了,繼續來完善我們的程式碼。之前的六篇完成了領域層、應用層、以及基礎結構層的部分程式碼,這篇打算搭建下UI層的程式碼。
DDD領域驅動設計初探系列文章:
一、UI層介紹
在DDD裡面,UI層的設計也分為BS和CS,本篇還是以Web為例來說明。我們的Web採用的是MVC+bootstrap的架構。Table元件使用的是bootstrap table,之所以用它是因為它的API比較全,並且博主覺得它的風格適用於各種型別的裝置,無論是PC端還是手機端都都能很好的相容各種瀏覽器。
這裡還是貼出bootstrap API的相關地址。
二、程式碼示例
上篇完成了WCF的設計程式碼,但是具體的業務邏輯的程式碼還沒有,我們先來實現具體業務的CURD程式碼。
1、WCF程式碼
1.1 WCF服務業務介面程式碼
/// <summary> /// 許可權管理模組介面契約 /// </summary> [ServiceContract] [ServiceInterface] public interface IPowerManageWCFService { #region 使用者管理 [OperationContract] List<DTO_TB_USERS> GetUsers(ExpressionNode expressionNode); [OperationContract] DTO_TB_USERS AddUser(DTO_TB_USERS oUser); [OperationContract] bool DeleteUser(DTO_TB_USERS oUser); [OperationContract] bool DeleteUserByLamada(ExpressionNode expressionNode); [OperationContract]bool UpdateUser(DTO_TB_USERS oUser); #endregion #region 部門管理 [OperationContract] List<DTO_TB_DEPARTMENT> GetDepartments(ExpressionNode expressionNode); [OperationContract] DTO_TB_DEPARTMENT AddDepartment(DTO_TB_DEPARTMENT oDept); [OperationContract] bool DeleteDepartment(DTO_TB_DEPARTMENT oDept); [OperationContract] bool DeleteDeptByLamada(ExpressionNode expressionNode); [OperationContract] bool UpdateDepartment(DTO_TB_DEPARTMENT oDept); #endregion #region 角色管理 [OperationContract] List<DTO_TB_ROLE> GetRoles(ExpressionNode expressionNode); [OperationContract] DTO_TB_ROLE AddRole(DTO_TB_ROLE oRole); #endregion #region 選單管理 [OperationContract] List<DTO_TB_MENU> GetMenus(ExpressionNode expressionNode); [OperationContract] DTO_TB_MENU AddMenu(DTO_TB_MENU oMenu); #endregion }
1.2 WCF介面實現程式碼:
[ServiceClass] public class PowerManageWCFService :BaseService, IPowerManageWCFService { #region Fields [Import] private IUserRepository userRepository { get; set; } [Import] private IDepartmentRepository departmentRepository { get; set; } [Import] private IRoleRepository roleRepository { get; set; } [Import] private IMenuRepository menuRepository { get; set; } #endregion #region Constust public PowerManageWCFService() { } #endregion #region WCF服務介面實現 #region 使用者管理 //這裡引數為什麼不直接用Expression<Func<DTO_TB_USERS,bool>>這種型別,是因為Expression不支援序列化,無法用於WCF資料的傳遞 public List<DTO_TB_USERS> GetUsers(ExpressionNode expressionNode) { Expression<Func<DTO_TB_USERS, bool>> selector = null; if (expressionNode != null) { selector = expressionNode.ToExpression<Func<DTO_TB_USERS, bool>>(); } var lstRes = base.GetDtoByLamada<DTO_TB_USERS, TB_USERS>(userRepository, selector); return lstRes; } public DTO_TB_USERS AddUser(DTO_TB_USERS oUser) { return base.AddDto<DTO_TB_USERS, TB_USERS>(userRepository, oUser); } public bool DeleteUser(DTO_TB_USERS oUser) { var bRes = false; try { base.DeleteDto<DTO_TB_USERS, TB_USERS>(userRepository, oUser); bRes = true; } catch { } return bRes; } public bool DeleteUserByLamada(ExpressionNode expressionNode) { Expression<Func<DTO_TB_USERS, bool>> selector = null; if (expressionNode != null) { selector = expressionNode.ToExpression<Func<DTO_TB_USERS, bool>>(); } var bRes = false; try { base.DeleteDto<DTO_TB_USERS, TB_USERS>(userRepository, selector); bRes = true; } catch { } return bRes; } public bool UpdateUser(DTO_TB_USERS oUser) { var bRes = false; try { base.UpdateDto<DTO_TB_USERS, TB_USERS>(userRepository, oUser); bRes = true; } catch { } return bRes; } #endregion #region 部門管理 public List<DTO_TB_DEPARTMENT> GetDepartments(ExpressionNode expressionNode) { Expression<Func<DTO_TB_DEPARTMENT, bool>> selector = null; if (expressionNode != null) { selector = expressionNode.ToExpression<Func<DTO_TB_DEPARTMENT, bool>>(); } return base.GetDtoByLamada<DTO_TB_DEPARTMENT, TB_DEPARTMENT>(departmentRepository, selector); } public DTO_TB_DEPARTMENT AddDepartment(DTO_TB_DEPARTMENT oDept) { return base.AddDto<DTO_TB_DEPARTMENT, TB_DEPARTMENT>(departmentRepository, oDept); } public bool DeleteDepartment(DTO_TB_DEPARTMENT oDept) { var bRes = false; try { base.DeleteDto<DTO_TB_DEPARTMENT, TB_DEPARTMENT>(departmentRepository, oDept); bRes = true; } catch { } return bRes; } public bool DeleteDeptByLamada(ExpressionNode expressionNode) { Expression<Func<DTO_TB_DEPARTMENT, bool>> selector = null; if (expressionNode != null) { selector = expressionNode.ToExpression<Func<DTO_TB_DEPARTMENT, bool>>(); } var bRes = false; try { base.DeleteDto<DTO_TB_DEPARTMENT, TB_DEPARTMENT>(departmentRepository, selector); bRes = true; } catch { } return bRes; } public bool UpdateDepartment(DTO_TB_DEPARTMENT oDept) { var bRes = false; try { base.UpdateDto<DTO_TB_DEPARTMENT, TB_DEPARTMENT>(departmentRepository, oDept); bRes = true; } catch { } return bRes; } #endregion #region 角色管理 public List<DTO_TB_ROLE> GetRoles(ExpressionNode expressionNode) { Expression<Func<DTO_TB_ROLE, bool>> selector = null; if (expressionNode != null) { selector = expressionNode.ToExpression<Func<DTO_TB_ROLE, bool>>(); } return base.GetDtoByLamada<DTO_TB_ROLE, TB_ROLE>(roleRepository, selector); } public DTO_TB_ROLE AddRole(DTO_TB_ROLE oRole) { return base.AddDto<DTO_TB_ROLE, TB_ROLE>(roleRepository, oRole); } #endregion #region 選單管理 public List<DTO_TB_MENU> GetMenus(ExpressionNode expressionNode) { Expression<Func<DTO_TB_MENU, bool>> selector = null; if (expressionNode != null) { selector = expressionNode.ToExpression<Func<DTO_TB_MENU, bool>>(); } return base.GetDtoByLamada<DTO_TB_MENU, TB_MENU>(menuRepository, selector); } public DTO_TB_MENU AddMenu(DTO_TB_MENU oMenu) { return base.AddDto<DTO_TB_MENU, TB_MENU>(menuRepository, oMenu); } #endregion #endregion }PowerManageWCFService
這裡要說明一點,在通過lamada表示式查詢的方法裡面為什麼不直接用Expression<Func<DTO_TB_USERS,bool>>這種型別,而要使用ExpressionNode這種型別的變數呢?
這是因為Expression不支援序列化,無法用於WCF資料的傳遞。ExpressionNode這個物件的使用需要新增Serialize.Linq這個dll的引用,還好有我們神奇的NuGet,讓我們再也不用去網上找一大堆的dll了。
我們公用的增刪改查封裝到了BaseService這個父類裡面。
1.3 BaseService程式碼
public class BaseService { #region Fields private bool bInitAutoMapper = false; #endregion #region Construct public BaseService() { //註冊MEF Regisgter.regisgter().ComposeParts(this); } #endregion #region 查詢 /// <summary> /// 通用單表查詢方法 /// </summary> /// <typeparam name="DtoModel">DTOmodel</typeparam> /// <typeparam name="DomainModel">領域模型</typeparam> /// <param name="oRepository">需要傳過來的倉儲介面物件</param> /// <param name="selector">前端傳過來的lamada表示式</param> /// <returns></returns> public List<DtoModel> GetDtoByLamada<DtoModel, DomainModel>(IRepository<DomainModel> oRepository, Expression<Func<DtoModel, bool>> selector = null) where DomainModel : AggregateRoot where DtoModel : Dto_BaseModel { InitAutoMapper<DtoModel, DomainModel>(); if (selector == null) { var lstDomainModel = oRepository.Entities.ToList(); return Mapper.Map<List<DomainModel>, List<DtoModel>>(lstDomainModel); } //得到從Web傳過來和DTOModel相關的lamaba表示式的委託 Func<DtoModel, bool> match = selector.Compile(); //建立對映Expression的委託 Func<DomainModel, DtoModel> mapper = AutoMapper.QueryableExtensions.Extensions.CreateMapExpression<DomainModel, DtoModel>(Mapper.Engine).Compile(); //得到領域Model相關的lamada Expression<Func<DomainModel, bool>> lamada = ef_t => match(mapper(ef_t)); List<DomainModel> list = oRepository.Find(lamada).ToList(); return Mapper.Map<List<DomainModel>, List<DtoModel>>(list); } #endregion #region 新增 public DtoModel AddDto<DtoModel, DomainModel>(IRepository<DomainModel> oRepository, DtoModel oDtoModel) where DomainModel : AggregateRoot where DtoModel : Dto_BaseModel { InitAutoMapper<DtoModel, DomainModel>(); var oDomain = Mapper.Map<DtoModel, DomainModel>(oDtoModel); oRepository.Insert(oDomain); return Mapper.Map<DomainModel, DtoModel>(oDomain); } #endregion #region 刪除 public int DeleteDto<DtoModel, DomainModel>(IRepository<DomainModel> oRepository, DtoModel oDtoModel) where DomainModel : AggregateRoot where DtoModel : Dto_BaseModel { InitAutoMapper<DtoModel, DomainModel>(); var oDomain = Mapper.Map<DtoModel, DomainModel>(oDtoModel); return oRepository.Delete(oDomain); } public int DeleteDto<DtoModel, DomainModel>(IRepository<DomainModel> oRepository, Expression<Func<DtoModel, bool>> selector = null) where DomainModel : AggregateRoot where DtoModel : Dto_BaseModel { InitAutoMapper<DtoModel, DomainModel>(); if (selector == null) { return 0; } //得到從Web傳過來和DTOModel相關的lamaba表示式的委託 Func<DtoModel, bool> match = selector.Compile(); //建立對映Expression的委託 Func<DomainModel, DtoModel> mapper = AutoMapper.QueryableExtensions.Extensions.CreateMapExpression<DomainModel, DtoModel>(Mapper.Engine).Compile(); //得到領域Model相關的lamada Expression<Func<DomainModel, bool>> lamada = ef_t => match(mapper(ef_t)); return oRepository.Delete(lamada); } #endregion #region 更新 public void UpdateDto<DtoModel, DomainModel>(IRepository<DomainModel> oRepository, DtoModel oDtoModel) where DomainModel : AggregateRoot where DtoModel : Dto_BaseModel { InitAutoMapper<DtoModel, DomainModel>(); var oDomain = Mapper.Map<DtoModel, DomainModel>(oDtoModel); oRepository.Update(oDomain); } #endregion #region Private private void InitAutoMapper<DtoModel, DomainModel>() { var oType = Mapper.FindTypeMapFor<DtoModel, DomainModel>(); if (oType==null) { Mapper.CreateMap<DtoModel, DomainModel>(); Mapper.CreateMap<DomainModel, DtoModel>(); } } #endregion }BaseService
這個父類主要做了兩件事:一是MEF的初始化;二是通用增刪改查的實現。所有dto物件和領域model的對映都在這裡統一管理。
2、UI層程式碼
UI層裡面,為了更好分離程式碼,我們引入了介面程式設計的機制,引入了ESTM.Web.IBLL和ESTM.Web.BLL兩個專案,如圖:
2.1 ESTM.Web.IBLL程式碼
這個dll主要定義介面規則。
public interface IPowerManager { List<DTO_TB_USERS> GetUsers(Expression<Func<DTO_TB_USERS, bool>> selector = null); DTO_TB_USERS AddUser(DTO_TB_USERS oUser); bool DeleteUser(DTO_TB_USERS oUser); bool UpdateUser(DTO_TB_USERS oUser); bool DeleteUser(Expression<Func<DTO_TB_USERS, bool>> selector = null); List<DTO_TB_DEPARTMENT> GetDepartments(Expression<Func<DTO_TB_DEPARTMENT, bool>> selector = null); DTO_TB_DEPARTMENT AddDepartment(DTO_TB_DEPARTMENT oDept); bool DeleteDepartment(DTO_TB_DEPARTMENT oDept); bool DeleteDepartment(Expression<Func<DTO_TB_DEPARTMENT, bool>> selector = null); bool UpdateDepartment(DTO_TB_DEPARTMENT oDept); List<DTO_TB_ROLE> GetRoles(Expression<Func<DTO_TB_ROLE, bool>> selector = null); List<DTO_TB_MENU> GetMenus(Expression<Func<DTO_TB_MENU, bool>> selector = null); }
2.2 ESTM.Web.BLL程式碼
這個dll用於實現ESTM.Web.IBLL裡面的介面方法
[Export(typeof(IPowerManager))] public class PowerManager : IPowerManager { #region Fields //建立WCF服務連線物件 private ServiceReference_PowerManager.PowerManageWCFServiceClient oService = CreatePowerManagerService.GetInstance(); #endregion #region 介面實現 public List<DTO_TB_USERS> GetUsers(Expression<Func<Common.DtoModel.DTO_TB_USERS, bool>> selector = null) { return oService.GetUsers(GetExpressionNode<DTO_TB_USERS>(selector)); } public List<Common.DtoModel.DTO_TB_DEPARTMENT> GetDepartments(Expression<Func<Common.DtoModel.DTO_TB_DEPARTMENT, bool>> selector = null) { return oService.GetDepartments(GetExpressionNode<DTO_TB_DEPARTMENT>(selector)); } public List<Common.DtoModel.DTO_TB_ROLE> GetRoles(Expression<Func<Common.DtoModel.DTO_TB_ROLE, bool>> selector = null) { return oService.GetRoles(GetExpressionNode<DTO_TB_ROLE>(selector)); } public List<Common.DtoModel.DTO_TB_MENU> GetMenus(Expression<Func<Common.DtoModel.DTO_TB_MENU, bool>> selector = null) { return oService.GetMenus(GetExpressionNode<DTO_TB_MENU>(selector)); } #endregion #region Privates //將lamada表示式轉換為可用於WCF傳遞的ExpressionNode型別 private ExpressionNode GetExpressionNode<Dto>(Expression<Func<Dto,bool>> selector) { if (selector == null) { return null; } ExpressionConverter expressionConverter = new ExpressionConverter(); ExpressionNode expressionNode = expressionConverter.Convert(selector); return expressionNode; } #endregion public DTO_TB_USERS AddUser(DTO_TB_USERS oUser) { return oService.AddUser(oUser); } public bool DeleteUser(DTO_TB_USERS oUser) { return oService.DeleteUser(oUser); } public bool DeleteUser(Expression<Func<DTO_TB_USERS, bool>> selector = null) { if (selector == null) { return false; } ExpressionConverter expressionConverter = new ExpressionConverter(); ExpressionNode expressionNode = expressionConverter.Convert(selector); return oService.DeleteUserByLamada(expressionNode); } public bool UpdateUser(DTO_TB_USERS oUser) { return oService.UpdateUser(oUser); } public DTO_TB_DEPARTMENT AddDepartment(DTO_TB_DEPARTMENT oDept) { return oService.AddDepartment(oDept); } public bool DeleteDepartment(DTO_TB_DEPARTMENT oDept) { return oService.DeleteDepartment(oDept); } public bool UpdateDepartment(DTO_TB_DEPARTMENT oDept) { return oService.UpdateDepartment(oDept); } public bool DeleteDepartment(Expression<Func<DTO_TB_DEPARTMENT, bool>> selector = null) { if (selector == null) { return false; } ExpressionConverter expressionConverter = new ExpressionConverter(); ExpressionNode expressionNode = expressionConverter.Convert(selector); return oService.DeleteDeptByLamada(expressionNode); } }PowerManager : IPowerManager
public class CreatePowerManagerService { private static ServiceReference_PowerManager.PowerManageWCFServiceClient oPowerManagerClient = null; private static object obj = new object(); public static ServiceReference_PowerManager.PowerManageWCFServiceClient GetInstance() { lock (obj) { if (oPowerManagerClient == null) { oPowerManagerClient = new ServiceReference_PowerManager.PowerManageWCFServiceClient(); } } return oPowerManagerClient; } }
由於是採用的新增服務引用的方式引用的WCF服務,所以在這一層需要新增WCF服務的引用。在實現這部分程式碼的時候博主遇到過一個問題,在此和朋友們分享一下。由於在WCF服務的設計裡面用到了DTO物件,而在ESTM.Web.BLL這個專案裡面也要用到DTO,但是新增WCF服務引用的時候預設的是WCF服務裡面的DTO,而不是ESTM.Common.DtoModel這個專案的DTO物件,這樣就有問題了,每次如果我們需要改動下dto的內容,那麼我們就需要更新下服務引用。還好,微軟給我們選擇的機制,我們來看圖
這樣就能解決上面的問題了。
2.3 ESTM.Web程式碼
按照面向介面的機制,ESTM.Web專案是不需要新增ESTM.Web.BLL這個實現層專案引用的,通過MEF動態匯入ESTM.Web.BLL裡面的物件。我們來看程式碼:
public class PowerManagerController : BaseController { [Import] private IPowerManager PowerManager { set; get; } #region Views // GET: PowerManager public ActionResult User() { return View(); } public ActionResult Role() { return View(); } public ActionResult Menu() { return View(); } public ActionResult Department() { return View(); } #endregion #region 部門管理 public JsonResult GetDepartments(int limit, int offset, string departmentname, string statu) { //得到lamada表示式 var oLamadaExtention = new LamadaExtention<DTO_TB_DEPARTMENT>(); if (!string.IsNullOrEmpty(departmentname)) { oLamadaExtention.GetExpression("DEPARTMENT_NAME", departmentname, ExpressionType.Contains); } if (!string.IsNullOrEmpty(statu)) { oLamadaExtention.GetExpression("STATUS", statu, ExpressionType.Contains); } var lamada = oLamadaExtention.GetLambda(); var lstRes = PowerManager.GetDepartments(lamada); return Json(new { rows = lstRes.Skip(offset).Take(limit).ToList(), total = lstRes.Count }, JsonRequestBehavior.AllowGet); } public object GetDepartmentEdit(string strPostData) { var oDepartment = Newtonsoft.Json.JsonConvert.DeserializeObject<DTO_TB_DEPARTMENT>(strPostData); if (string.IsNullOrEmpty(oDepartment.DEPARTMENT_ID)) { oDepartment.DEPARTMENT_ID = Guid.NewGuid().ToString(); oDepartment = PowerManager.AddDepartment(oDepartment); } else { PowerManager.UpdateDepartment(oDepartment); } return oDepartment; } public object DeleteDept(string strID) { PowerManager.DeleteDepartment(x=>x.DEPARTMENT_ID == strID); return new object(); } #endregion #region 選單管理 public JsonResult GetMenus(int limit, int offset, string menuname, string menuurl) { var oLamadaExtention = new LamadaExtention<DTO_TB_MENU>(); if (!string.IsNullOrEmpty(menuname)) { oLamadaExtention.GetExpression("MENU_NAME", menuname, ExpressionType.Contains); } if (!string.IsNullOrEmpty(menuurl)) { oLamadaExtention.GetExpression("MENU_URL", menuurl, ExpressionType.Contains); } var lamada = oLamadaExtention.GetLambda(); var lstRes = PowerManager.GetMenus(lamada).ToList(); return Json(new { rows = lstRes.Skip(offset).Take(limit).ToList(), total = lstRes.Count }, JsonRequestBehavior.AllowGet); } public object GetMenuEdit(string strPostData) { var oMenu = Newtonsoft.Json.JsonConvert.DeserializeObject<DTO_TB_MENU>(strPostData); if (string.IsNullOrEmpty(oMenu.MENU_ID)) { //oMenu = MenuManager.Add(oMenu); } else { //MenuManager.Update(oMenu); } return oMenu; } public object DeleteMenu(string strID) { //MenuManager.Delete(strID); return new object(); } public object GetParentMenu() { var lstMenu = PowerManager.GetMenus(x => x.MENU_LEVEL == "1"); //var lstRes = RoleManager.Find().ToList(); //var oRes = new PageRowData(); //oRes.rows = lstRes.Skip(offset).Take(limit).ToList(); //oRes.total = lstRes.Count; return lstMenu; ; } public object GetChildrenMenu(string strParentID) { var lstMenu = PowerManager.GetMenus(x => x.MENU_LEVEL == "2" && x.PARENT_ID == strParentID).ToList(); //var lstRes = RoleManager.Find().ToList(); //var oRes = new PageRowData(); //oRes.rows = lstRes.Skip(offset).Take(limit).ToList(); //oRes.total = lstRes.Count; return lstMenu; ; } #endregion #region 許可權管理 public JsonResult GetRole(int limit, int offset, string rolename, string desc) { var oLamadaExtention = new LamadaExtention<DTO_TB_ROLE>(); if (!string.IsNullOrEmpty(rolename)) { oLamadaExtention.GetExpression("ROLE_NAME", rolename, ExpressionType.Contains); } if (!string.IsNullOrEmpty(desc)) { oLamadaExtention.GetExpression("DESCRIPTION", desc, ExpressionType.Contains); } var lamada = oLamadaExtention.GetLambda(); var lstRes = PowerManager.GetRoles(lamada).ToList(); return Json(new { rows = lstRes.Skip(offset).Take(limit).ToList(), total = lstRes.Count }, JsonRequestBehavior.AllowGet); } #endregion #region 使用者管理 public JsonResult GetUsers(int limit, int offset, string username, string fullname) { var oLamadaExtention = new LamadaExtention<DTO_TB_USERS>(); if (!string.IsNullOrEmpty(username)) { oLamadaExtention.GetExpression("USER_NAME", username, ExpressionType.Contains); } if (!string.IsNullOrEmpty(fullname)) { oLamadaExtention.GetExpression("FULLNAME", fullname, ExpressionType.Contains); } var lamada = oLamadaExtention.GetLambda(); var lstRes = PowerManager.GetUsers(lamada).ToList(); return Json(new { rows = lstRes.Skip(offset).Take(limit).ToList(), total = lstRes.Count }, JsonRequestBehavior.AllowGet); } public object GetUserEdit(string strPostData) { var oUser = Newtonsoft.Json.JsonConvert.DeserializeObject<DTO_TB_USERS>(strPostData); if (string.IsNullOrEmpty(oUser.USER_ID)) { oUser.USER_ID = Guid.NewGuid().ToString(); oUser = PowerManager.AddUser(oUser); } else { PowerManager.UpdateUser(oUser); } return oUser; } public object DeleteUser(string strID) { PowerManager.DeleteUser(x => x.USER_ID == strID); return new object(); } #endregion }PowerManagerController
View頁面
<!DOCTYPE html> <html> <head> <meta name="viewport"相關推薦
no