标签 标签 标签

  • 一句话的事儿:

前言:好久没更新博客了,每天被该死的业务缠身,今天正好一个模块完成了,继续来完善我们的代码。之前的六篇完成了领域层、应用层、以及基础结构层的部分代码,这篇打算搭建下UI层的代码。
DDD领域驱动设计初探系列文章:

  • C#进阶系列——DDD领域驱动设计初探(一):聚合
  • C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上)
  • C#进阶系列——DDD领域驱动设计初探(三):仓储Repository(下)
  • C#进阶系列——DDD领域驱动设计初探(四):WCF搭建
  • C#进阶系列——DDD领域驱动设计初探(五):AutoMapper使用
  • C#进阶系列——DDD领域驱动设计初探(六):领域服务
  • C#进阶系列——DDD领域驱动设计初探(七):Web层的搭建

    一、UI层介绍

    在DDD里面,UI层的设计也分为BS和CS,本篇还是以Web为例来说明。我们的Web采用的是MVC+bootstrap的架构。Table组件使用的是bootstrap table,之所以用它是因为它的API比较全,并且博主觉得它的风格适用于各种类型的设备,无论是PC端还是手机端都都能很好的兼容各种浏览器。
    这里还是贴出bootstrap API的相关地址。
    Bootstrap中文网:http://www.bootcss.com/
    Bootstrap Table Demo:http://issues.wenzhixin.net.cn/bootstrap-table/index.html
    Bootstrap Table API:http://bootstrap-table.wenzhixin.net.cn/zh-cn/documentation/
    Bootstrap Table源码:https://github.com/wenzhixin/bootstrap-table
    Bootstrap DataPicker:http://www.bootcss.com/p/bootstrap-datetimepicker/
    Bootstrap离线API

    二、代码示例

    上篇完成了WCF的设计代码,但是具体的业务逻辑的代码还没有,我们先来实现具体业务的CURD代码。

    1、WCF代码

    1.1 WCF服务业务接口代码

    1. /// <summary>
    2. /// 权限管理模块接口契约
    3. /// </summary>
    4. [ServiceContract]
    5. [ServiceInterface]
    6. public interface IPowerManageWCFService
    7. {
    8. #region 用户管理
    9. [OperationContract]
    10. List<DTO_TB_USERS> GetUsers(ExpressionNode expressionNode);
    11. [OperationContract]
    12. DTO_TB_USERS AddUser(DTO_TB_USERS oUser);
    13. [OperationContract]
    14. bool DeleteUser(DTO_TB_USERS oUser);
    15. [OperationContract]
    16. bool DeleteUserByLamada(ExpressionNode expressionNode);
    17. [OperationContract]
    18. bool UpdateUser(DTO_TB_USERS oUser);
    19. #endregion
    20. #region 部门管理
    21. [OperationContract]
    22. List<DTO_TB_DEPARTMENT> GetDepartments(ExpressionNode expressionNode);
    23. [OperationContract]
    24. DTO_TB_DEPARTMENT AddDepartment(DTO_TB_DEPARTMENT oDept);
    25. [OperationContract]
    26. bool DeleteDepartment(DTO_TB_DEPARTMENT oDept);
    27. [OperationContract]
    28. bool DeleteDeptByLamada(ExpressionNode expressionNode);
    29. [OperationContract]
    30. bool UpdateDepartment(DTO_TB_DEPARTMENT oDept);
    31. #endregion
    32. #region 角色管理
    33. [OperationContract]
    34. List<DTO_TB_ROLE> GetRoles(ExpressionNode expressionNode);
    35. [OperationContract]
    36. DTO_TB_ROLE AddRole(DTO_TB_ROLE oRole);
    37. #endregion
    38. #region 菜单管理
    39. [OperationContract]
    40. List<DTO_TB_MENU> GetMenus(ExpressionNode expressionNode);
    41. [OperationContract]
    42. DTO_TB_MENU AddMenu(DTO_TB_MENU oMenu);
    43. #endregion
    44. }

    1.2 WCF接口实现代码:

    ```csharp [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>这种类型,是因为Expression不支持序列化,无法用于WCF数据的传递 public List GetUsers(ExpressionNode expressionNode) {

    1. Expression<Func<DTO_TB_USERS, bool>> selector = null;
    2. if (expressionNode != null)
    3. {
    4. selector = expressionNode.ToExpression<Func<DTO_TB_USERS, bool>>();
    5. }
    6. var lstRes = base.GetDtoByLamada<DTO_TB_USERS, TB_USERS>(userRepository, selector);
    7. return lstRes;

    }

    public DTO_TB_USERS AddUser(DTO_TB_USERS oUser) {

    1. return base.AddDto<DTO_TB_USERS, TB_USERS>(userRepository, oUser);

    }

    public bool DeleteUser(DTO_TB_USERS oUser) {

    1. var bRes = false;
    2. try
    3. {
    4. base.DeleteDto<DTO_TB_USERS, TB_USERS>(userRepository, oUser);
    5. bRes = true;
    6. }
    7. catch
    8. {
    9. }
    10. return bRes;

    }

    public bool DeleteUserByLamada(ExpressionNode expressionNode) {

    1. Expression<Func<DTO_TB_USERS, bool>> selector = null;
    2. if (expressionNode != null)
    3. {
    4. selector = expressionNode.ToExpression<Func<DTO_TB_USERS, bool>>();
    5. }
    6. var bRes = false;
    7. try
    8. {
    9. base.DeleteDto<DTO_TB_USERS, TB_USERS>(userRepository, selector);
    10. bRes = true;
    11. }
    12. catch
    13. {
    14. }
    15. return bRes;

    }

    public bool UpdateUser(DTO_TB_USERS oUser) {

    1. var bRes = false;
    2. try
    3. {
    4. base.UpdateDto<DTO_TB_USERS, TB_USERS>(userRepository, oUser);
    5. bRes = true;
    6. }
    7. catch
    8. {
    9. }
    10. return bRes;

    }

    endregion

    region 部门管理

    public List GetDepartments(ExpressionNode expressionNode) {

    1. Expression<Func<DTO_TB_DEPARTMENT, bool>> selector = null;
    2. if (expressionNode != null)
    3. {
    4. selector = expressionNode.ToExpression<Func<DTO_TB_DEPARTMENT, bool>>();
    5. }
    6. return base.GetDtoByLamada<DTO_TB_DEPARTMENT, TB_DEPARTMENT>(departmentRepository, selector);

    } public DTO_TB_DEPARTMENT AddDepartment(DTO_TB_DEPARTMENT oDept) {

    1. return base.AddDto<DTO_TB_DEPARTMENT, TB_DEPARTMENT>(departmentRepository, oDept);

    }

    public bool DeleteDepartment(DTO_TB_DEPARTMENT oDept) {

    1. var bRes = false;
    2. try
    3. {
    4. base.DeleteDto<DTO_TB_DEPARTMENT, TB_DEPARTMENT>(departmentRepository, oDept);
    5. bRes = true;
    6. }
    7. catch
    8. {
    9. }
    10. return bRes;

    }

    public bool DeleteDeptByLamada(ExpressionNode expressionNode) {

    1. Expression<Func<DTO_TB_DEPARTMENT, bool>> selector = null;
    2. if (expressionNode != null)
    3. {
    4. selector = expressionNode.ToExpression<Func<DTO_TB_DEPARTMENT, bool>>();
    5. }
    6. var bRes = false;
    7. try
    8. {
    9. base.DeleteDto<DTO_TB_DEPARTMENT, TB_DEPARTMENT>(departmentRepository, selector);
    10. bRes = true;
    11. }
    12. catch
    13. {
    14. }
    15. return bRes;

    }

    public bool UpdateDepartment(DTO_TB_DEPARTMENT oDept) {

    1. var bRes = false;
    2. try
    3. {
    4. base.UpdateDto<DTO_TB_DEPARTMENT, TB_DEPARTMENT>(departmentRepository, oDept);
    5. bRes = true;
    6. }
    7. catch
    8. {
    9. }
    10. return bRes;

    }

    endregion

    region 角色管理

    public List GetRoles(ExpressionNode expressionNode) {

    1. Expression<Func<DTO_TB_ROLE, bool>> selector = null;
    2. if (expressionNode != null)
    3. {
    4. selector = expressionNode.ToExpression<Func<DTO_TB_ROLE, bool>>();
    5. }
    6. return base.GetDtoByLamada<DTO_TB_ROLE, TB_ROLE>(roleRepository, selector);

    }

    public DTO_TB_ROLE AddRole(DTO_TB_ROLE oRole) {

    1. return base.AddDto<DTO_TB_ROLE, TB_ROLE>(roleRepository, oRole);

    }

    endregion

    region 菜单管理

    public List GetMenus(ExpressionNode expressionNode) {

    1. Expression<Func<DTO_TB_MENU, bool>> selector = null;
    2. if (expressionNode != null)
    3. {
    4. selector = expressionNode.ToExpression<Func<DTO_TB_MENU, bool>>();
    5. }
    6. return base.GetDtoByLamada<DTO_TB_MENU, TB_MENU>(menuRepository, selector);

    }

    public DTO_TB_MENU AddMenu(DTO_TB_MENU oMenu) {

    1. return base.AddDto<DTO_TB_MENU, TB_MENU>(menuRepository, oMenu);

    }

    endregion

    endregion

    }

PowerManageWCFService

  1. 这里要说明一点,在通过lamada表达式查询的方法里面为什么不直接用Expression<Func<DTO_TB_USERS,bool>>这种类型,而要使用ExpressionNode这种类型的变量呢?<br />这是因为Expression不支持序列化,无法用于WCF数据的传递。ExpressionNode这个对象的使用需要添加Serialize.Linq这个dll的引用,还好有我们神奇的NuGet,让我们再也不用去网上找一大堆的dll了。<br />![](https://cdn.nlark.com/yuque/0/2021/png/446847/1618381575862-918f4de4-6f22-4b31-9c1d-5d778257ef30.png#align=left&display=inline&height=355&margin=%5Bobject%20Object%5D&originHeight=355&originWidth=1001&size=0&status=done&style=none&width=1001)<br />我们公用的增删改查封装到了BaseService这个父类里面。
  2. <a name="BTkUW"></a>
  3. #### 1.3 BaseService代码
  4. ```csharp
  5. public class BaseService
  6. {
  7. #region Fields
  8. private bool bInitAutoMapper = false;
  9. #endregion
  10. #region Construct
  11. public BaseService()
  12. {
  13. //注册MEF
  14. Regisgter.regisgter().ComposeParts(this);
  15. }
  16. #endregion
  17. #region 查询
  18. /// <summary>
  19. /// 通用单表查询方法
  20. /// </summary>
  21. /// <typeparam name="DtoModel">DTOmodel</typeparam>
  22. /// <typeparam name="DomainModel">领域模型</typeparam>
  23. /// <param name="oRepository">需要传过来的仓储接口对象</param>
  24. /// <param name="selector">前端传过来的lamada表达式</param>
  25. /// <returns></returns>
  26. public List<DtoModel> GetDtoByLamada<DtoModel, DomainModel>(IRepository<DomainModel> oRepository, Expression<Func<DtoModel, bool>> selector = null)
  27. where DomainModel : AggregateRoot
  28. where DtoModel : Dto_BaseModel
  29. {
  30. InitAutoMapper<DtoModel, DomainModel>();
  31. if (selector == null)
  32. {
  33. var lstDomainModel = oRepository.Entities.ToList();
  34. return Mapper.Map<List<DomainModel>, List<DtoModel>>(lstDomainModel);
  35. }
  36. //得到从Web传过来和DTOModel相关的lamaba表达式的委托
  37. Func<DtoModel, bool> match = selector.Compile();
  38. //创建映射Expression的委托
  39. Func<DomainModel, DtoModel> mapper = AutoMapper.QueryableExtensions.Extensions.CreateMapExpression<DomainModel, DtoModel>(Mapper.Engine).Compile();
  40. //得到领域Model相关的lamada
  41. Expression<Func<DomainModel, bool>> lamada = ef_t => match(mapper(ef_t));
  42. List<DomainModel> list = oRepository.Find(lamada).ToList();
  43. return Mapper.Map<List<DomainModel>, List<DtoModel>>(list);
  44. }
  45. #endregion
  46. #region 新增
  47. public DtoModel AddDto<DtoModel, DomainModel>(IRepository<DomainModel> oRepository, DtoModel oDtoModel)
  48. where DomainModel : AggregateRoot
  49. where DtoModel : Dto_BaseModel
  50. {
  51. InitAutoMapper<DtoModel, DomainModel>();
  52. var oDomain = Mapper.Map<DtoModel, DomainModel>(oDtoModel);
  53. oRepository.Insert(oDomain);
  54. return Mapper.Map<DomainModel, DtoModel>(oDomain);
  55. }
  56. #endregion
  57. #region 删除
  58. public int DeleteDto<DtoModel, DomainModel>(IRepository<DomainModel> oRepository, DtoModel oDtoModel)
  59. where DomainModel : AggregateRoot
  60. where DtoModel : Dto_BaseModel
  61. {
  62. InitAutoMapper<DtoModel, DomainModel>();
  63. var oDomain = Mapper.Map<DtoModel, DomainModel>(oDtoModel);
  64. return oRepository.Delete(oDomain);
  65. }
  66. public int DeleteDto<DtoModel, DomainModel>(IRepository<DomainModel> oRepository, Expression<Func<DtoModel, bool>> selector = null)
  67. where DomainModel : AggregateRoot
  68. where DtoModel : Dto_BaseModel
  69. {
  70. InitAutoMapper<DtoModel, DomainModel>();
  71. if (selector == null)
  72. {
  73. return 0;
  74. }
  75. //得到从Web传过来和DTOModel相关的lamaba表达式的委托
  76. Func<DtoModel, bool> match = selector.Compile();
  77. //创建映射Expression的委托
  78. Func<DomainModel, DtoModel> mapper = AutoMapper.QueryableExtensions.Extensions.CreateMapExpression<DomainModel, DtoModel>(Mapper.Engine).Compile();
  79. //得到领域Model相关的lamada
  80. Expression<Func<DomainModel, bool>> lamada = ef_t => match(mapper(ef_t));
  81. return oRepository.Delete(lamada);
  82. }
  83. #endregion
  84. #region 更新
  85. public void UpdateDto<DtoModel, DomainModel>(IRepository<DomainModel> oRepository, DtoModel oDtoModel)
  86. where DomainModel : AggregateRoot
  87. where DtoModel : Dto_BaseModel
  88. {
  89. InitAutoMapper<DtoModel, DomainModel>();
  90. var oDomain = Mapper.Map<DtoModel, DomainModel>(oDtoModel);
  91. oRepository.Update(oDomain);
  92. }
  93. #endregion
  94. #region Private
  95. private void InitAutoMapper<DtoModel, DomainModel>()
  96. {
  97. var oType = Mapper.FindTypeMapFor<DtoModel, DomainModel>();
  98. if (oType==null)
  99. {
  100. Mapper.CreateMap<DtoModel, DomainModel>();
  101. Mapper.CreateMap<DomainModel, DtoModel>();
  102. }
  103. }
  104. #endregion
  105. }
  106. BaseService

这个父类主要做了两件事:一是MEF的初始化;二是通用增删改查的实现。所有dto对象和领域model的映射都在这里统一管理。

2、UI层代码

UI层里面,为了更好分离代码,我们引入了接口编程的机制,引入了ESTM.Web.IBLL和ESTM.Web.BLL两个项目,如图:
DDD领域驱动设计初探(七):Web层的搭建 - 图1
为什么要有这么一个接口层?之前C#进阶系列——MEF实现设计上的“松耦合”(终结篇:面向接口编程)这篇已经做过介绍,对面向接口编程不了解的朋友可以看看。

2.1 ESTM.Web.IBLL代码

这个dll主要定义接口规则。

  1. public interface IPowerManager
  2. {
  3. List<DTO_TB_USERS> GetUsers(Expression<Func<DTO_TB_USERS, bool>> selector = null);
  4. DTO_TB_USERS AddUser(DTO_TB_USERS oUser);
  5. bool DeleteUser(DTO_TB_USERS oUser);
  6. bool UpdateUser(DTO_TB_USERS oUser);
  7. bool DeleteUser(Expression<Func<DTO_TB_USERS, bool>> selector = null);
  8. List<DTO_TB_DEPARTMENT> GetDepartments(Expression<Func<DTO_TB_DEPARTMENT, bool>> selector = null);
  9. DTO_TB_DEPARTMENT AddDepartment(DTO_TB_DEPARTMENT oDept);
  10. bool DeleteDepartment(DTO_TB_DEPARTMENT oDept);
  11. bool DeleteDepartment(Expression<Func<DTO_TB_DEPARTMENT, bool>> selector = null);
  12. bool UpdateDepartment(DTO_TB_DEPARTMENT oDept);
  13. List<DTO_TB_ROLE> GetRoles(Expression<Func<DTO_TB_ROLE, bool>> selector = null);
  14. List<DTO_TB_MENU> GetMenus(Expression<Func<DTO_TB_MENU, bool>> selector = null);
  15. }

2.2 ESTM.Web.BLL代码

这个dll用于实现ESTM.Web.IBLL里面的接口方法

  1. [Export(typeof(IPowerManager))]
  2. public class PowerManager : IPowerManager
  3. {
  4. #region Fields
  5. //创建WCF服务连接对象
  6. private ServiceReference_PowerManager.PowerManageWCFServiceClient oService = CreatePowerManagerService.GetInstance();
  7. #endregion
  8. #region 接口实现
  9. public List<DTO_TB_USERS> GetUsers(Expression<Func<Common.DtoModel.DTO_TB_USERS, bool>> selector = null)
  10. {
  11. return oService.GetUsers(GetExpressionNode<DTO_TB_USERS>(selector));
  12. }
  13. public List<Common.DtoModel.DTO_TB_DEPARTMENT> GetDepartments(Expression<Func<Common.DtoModel.DTO_TB_DEPARTMENT, bool>> selector = null)
  14. {
  15. return oService.GetDepartments(GetExpressionNode<DTO_TB_DEPARTMENT>(selector));
  16. }
  17. public List<Common.DtoModel.DTO_TB_ROLE> GetRoles(Expression<Func<Common.DtoModel.DTO_TB_ROLE, bool>> selector = null)
  18. {
  19. return oService.GetRoles(GetExpressionNode<DTO_TB_ROLE>(selector));
  20. }
  21. public List<Common.DtoModel.DTO_TB_MENU> GetMenus(Expression<Func<Common.DtoModel.DTO_TB_MENU, bool>> selector = null)
  22. {
  23. return oService.GetMenus(GetExpressionNode<DTO_TB_MENU>(selector));
  24. }
  25. #endregion
  26. #region Privates
  27. //将lamada表达式转换为可用于WCF传递的ExpressionNode类型
  28. private ExpressionNode GetExpressionNode<Dto>(Expression<Func<Dto,bool>> selector)
  29. {
  30. if (selector == null)
  31. {
  32. return null;
  33. }
  34. ExpressionConverter expressionConverter = new ExpressionConverter();
  35. ExpressionNode expressionNode = expressionConverter.Convert(selector);
  36. return expressionNode;
  37. }
  38. #endregion
  39. public DTO_TB_USERS AddUser(DTO_TB_USERS oUser)
  40. {
  41. return oService.AddUser(oUser);
  42. }
  43. public bool DeleteUser(DTO_TB_USERS oUser)
  44. {
  45. return oService.DeleteUser(oUser);
  46. }
  47. public bool DeleteUser(Expression<Func<DTO_TB_USERS, bool>> selector = null)
  48. {
  49. if (selector == null)
  50. {
  51. return false;
  52. }
  53. ExpressionConverter expressionConverter = new ExpressionConverter();
  54. ExpressionNode expressionNode = expressionConverter.Convert(selector);
  55. return oService.DeleteUserByLamada(expressionNode);
  56. }
  57. public bool UpdateUser(DTO_TB_USERS oUser)
  58. {
  59. return oService.UpdateUser(oUser);
  60. }
  61. public DTO_TB_DEPARTMENT AddDepartment(DTO_TB_DEPARTMENT oDept)
  62. {
  63. return oService.AddDepartment(oDept);
  64. }
  65. public bool DeleteDepartment(DTO_TB_DEPARTMENT oDept)
  66. {
  67. return oService.DeleteDepartment(oDept);
  68. }
  69. public bool UpdateDepartment(DTO_TB_DEPARTMENT oDept)
  70. {
  71. return oService.UpdateDepartment(oDept);
  72. }
  73. public bool DeleteDepartment(Expression<Func<DTO_TB_DEPARTMENT, bool>> selector = null)
  74. {
  75. if (selector == null)
  76. {
  77. return false;
  78. }
  79. ExpressionConverter expressionConverter = new ExpressionConverter();
  80. ExpressionNode expressionNode = expressionConverter.Convert(selector);
  81. return oService.DeleteDeptByLamada(expressionNode);
  82. }
  83. }
  84. PowerManager : IPowerManager
  1. public class CreatePowerManagerService
  2. {
  3. private static ServiceReference_PowerManager.PowerManageWCFServiceClient oPowerManagerClient = null;
  4. private static object obj = new object();
  5. public static ServiceReference_PowerManager.PowerManageWCFServiceClient GetInstance()
  6. {
  7. lock (obj)
  8. {
  9. if (oPowerManagerClient == null)
  10. {
  11. oPowerManagerClient = new ServiceReference_PowerManager.PowerManageWCFServiceClient();
  12. }
  13. }
  14. return oPowerManagerClient;
  15. }
  16. }

由于是采用的添加服务引用的方式引用的WCF服务,所以在这一层需要添加WCF服务的引用。在实现这部分代码的时候博主遇到过一个问题,在此和朋友们分享一下。由于在WCF服务的设计里面用到了DTO对象,而在ESTM.Web.BLL这个项目里面也要用到DTO,但是添加WCF服务引用的时候默认的是WCF服务里面的DTO,而不是ESTM.Common.DtoModel这个项目的DTO对象,这样就有问题了,每次如果我们需要改动下dto的内容,那么我们就需要更新下服务引用。还好,微软给我们选择的机制,我们来看图
DDD领域驱动设计初探(七):Web层的搭建 - 图2
DDD领域驱动设计初探(七):Web层的搭建 - 图3
这样就能解决上面的问题了。

2.3 ESTM.Web代码

按照面向接口的机制,ESTM.Web项目是不需要添加ESTM.Web.BLL这个实现层项目引用的,通过MEF动态导入ESTM.Web.BLL里面的对象。我们来看代码:

  1. public class PowerManagerController : BaseController
  2. {
  3. [Import]
  4. private IPowerManager PowerManager { set; get; }
  5. #region Views
  6. // GET: PowerManager
  7. public ActionResult User()
  8. {
  9. return View();
  10. }
  11. public ActionResult Role()
  12. {
  13. return View();
  14. }
  15. public ActionResult Menu()
  16. {
  17. return View();
  18. }
  19. public ActionResult Department()
  20. {
  21. return View();
  22. }
  23. #endregion
  24. #region 部门管理
  25. public JsonResult GetDepartments(int limit, int offset, string departmentname, string statu)
  26. {
  27. //得到lamada表达式
  28. var oLamadaExtention = new LamadaExtention<DTO_TB_DEPARTMENT>();
  29. if (!string.IsNullOrEmpty(departmentname))
  30. {
  31. oLamadaExtention.GetExpression("DEPARTMENT_NAME", departmentname, ExpressionType.Contains);
  32. }
  33. if (!string.IsNullOrEmpty(statu))
  34. {
  35. oLamadaExtention.GetExpression("STATUS", statu, ExpressionType.Contains);
  36. }
  37. var lamada = oLamadaExtention.GetLambda();
  38. var lstRes = PowerManager.GetDepartments(lamada);
  39. return Json(new { rows = lstRes.Skip(offset).Take(limit).ToList(), total = lstRes.Count }, JsonRequestBehavior.AllowGet);
  40. }
  41. public object GetDepartmentEdit(string strPostData)
  42. {
  43. var oDepartment = Newtonsoft.Json.JsonConvert.DeserializeObject<DTO_TB_DEPARTMENT>(strPostData);
  44. if (string.IsNullOrEmpty(oDepartment.DEPARTMENT_ID))
  45. {
  46. oDepartment.DEPARTMENT_ID = Guid.NewGuid().ToString();
  47. oDepartment = PowerManager.AddDepartment(oDepartment);
  48. }
  49. else
  50. {
  51. PowerManager.UpdateDepartment(oDepartment);
  52. }
  53. return oDepartment;
  54. }
  55. public object DeleteDept(string strID)
  56. {
  57. PowerManager.DeleteDepartment(x=>x.DEPARTMENT_ID == strID);
  58. return new object();
  59. }
  60. #endregion
  61. #region 菜单管理
  62. public JsonResult GetMenus(int limit, int offset, string menuname, string menuurl)
  63. {
  64. var oLamadaExtention = new LamadaExtention<DTO_TB_MENU>();
  65. if (!string.IsNullOrEmpty(menuname))
  66. {
  67. oLamadaExtention.GetExpression("MENU_NAME", menuname, ExpressionType.Contains);
  68. }
  69. if (!string.IsNullOrEmpty(menuurl))
  70. {
  71. oLamadaExtention.GetExpression("MENU_URL", menuurl, ExpressionType.Contains);
  72. }
  73. var lamada = oLamadaExtention.GetLambda();
  74. var lstRes = PowerManager.GetMenus(lamada).ToList();
  75. return Json(new { rows = lstRes.Skip(offset).Take(limit).ToList(), total = lstRes.Count }, JsonRequestBehavior.AllowGet);
  76. }
  77. public object GetMenuEdit(string strPostData)
  78. {
  79. var oMenu = Newtonsoft.Json.JsonConvert.DeserializeObject<DTO_TB_MENU>(strPostData);
  80. if (string.IsNullOrEmpty(oMenu.MENU_ID))
  81. {
  82. //oMenu = MenuManager.Add(oMenu);
  83. }
  84. else
  85. {
  86. //MenuManager.Update(oMenu);
  87. }
  88. return oMenu;
  89. }
  90. public object DeleteMenu(string strID)
  91. {
  92. //MenuManager.Delete(strID);
  93. return new object();
  94. }
  95. public object GetParentMenu()
  96. {
  97. var lstMenu = PowerManager.GetMenus(x => x.MENU_LEVEL == "1");
  98. //var lstRes = RoleManager.Find().ToList();
  99. //var oRes = new PageRowData();
  100. //oRes.rows = lstRes.Skip(offset).Take(limit).ToList();
  101. //oRes.total = lstRes.Count;
  102. return lstMenu; ;
  103. }
  104. public object GetChildrenMenu(string strParentID)
  105. {
  106. var lstMenu = PowerManager.GetMenus(x => x.MENU_LEVEL == "2" && x.PARENT_ID == strParentID).ToList();
  107. //var lstRes = RoleManager.Find().ToList();
  108. //var oRes = new PageRowData();
  109. //oRes.rows = lstRes.Skip(offset).Take(limit).ToList();
  110. //oRes.total = lstRes.Count;
  111. return lstMenu; ;
  112. }
  113. #endregion
  114. #region 权限管理
  115. public JsonResult GetRole(int limit, int offset, string rolename, string desc)
  116. {
  117. var oLamadaExtention = new LamadaExtention<DTO_TB_ROLE>();
  118. if (!string.IsNullOrEmpty(rolename))
  119. {
  120. oLamadaExtention.GetExpression("ROLE_NAME", rolename, ExpressionType.Contains);
  121. }
  122. if (!string.IsNullOrEmpty(desc))
  123. {
  124. oLamadaExtention.GetExpression("DESCRIPTION", desc, ExpressionType.Contains);
  125. }
  126. var lamada = oLamadaExtention.GetLambda();
  127. var lstRes = PowerManager.GetRoles(lamada).ToList();
  128. return Json(new { rows = lstRes.Skip(offset).Take(limit).ToList(), total = lstRes.Count }, JsonRequestBehavior.AllowGet);
  129. }
  130. #endregion
  131. #region 用户管理
  132. public JsonResult GetUsers(int limit, int offset, string username, string fullname)
  133. {
  134. var oLamadaExtention = new LamadaExtention<DTO_TB_USERS>();
  135. if (!string.IsNullOrEmpty(username))
  136. {
  137. oLamadaExtention.GetExpression("USER_NAME", username, ExpressionType.Contains);
  138. }
  139. if (!string.IsNullOrEmpty(fullname))
  140. {
  141. oLamadaExtention.GetExpression("FULLNAME", fullname, ExpressionType.Contains);
  142. }
  143. var lamada = oLamadaExtention.GetLambda();
  144. var lstRes = PowerManager.GetUsers(lamada).ToList();
  145. return Json(new { rows = lstRes.Skip(offset).Take(limit).ToList(), total = lstRes.Count }, JsonRequestBehavior.AllowGet);
  146. }
  147. public object GetUserEdit(string strPostData)
  148. {
  149. var oUser = Newtonsoft.Json.JsonConvert.DeserializeObject<DTO_TB_USERS>(strPostData);
  150. if (string.IsNullOrEmpty(oUser.USER_ID))
  151. {
  152. oUser.USER_ID = Guid.NewGuid().ToString();
  153. oUser = PowerManager.AddUser(oUser);
  154. }
  155. else
  156. {
  157. PowerManager.UpdateUser(oUser);
  158. }
  159. return oUser;
  160. }
  161. public object DeleteUser(string strID)
  162. {
  163. PowerManager.DeleteUser(x => x.USER_ID == strID);
  164. return new object();
  165. }
  166. #endregion
  167. }
  168. PowerManagerController

View页面

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta name="viewport" content="width=device-width" />
  5. <title>@ViewBag.Title</title>
  6. @Styles.Render("~/Content/css")
  7. @Styles.Render("~/Content/table-css")
  8. @Scripts.Render("~/bundles/jquery")
  9. @Scripts.Render("~/bundles/bootstrap")
  10. @Scripts.Render("~/bundles/bootstrap-table")
  11. @RenderSection("Scripts", false)
  12. </head>
  13. <body>
  14. @RenderBody()
  15. </body>
  16. </html>
  17. _Layout.cshtml
  1. @{
  2. ViewBag.Title = "部门管理";
  3. Layout = "~/Views/Shared/_Layout.cshtml";
  4. }
  5. @Scripts.Render("~/bundles/PowerManage/DepartmentManage")
  6. <div class="panel-body" style="padding-bottom:0px;">
  7. <div class="panel panel-default">
  8. <div class="panel-heading">查询条件</div>
  9. <div class="panel-body">
  10. <div class="row">
  11. <div class="col-md-4">
  12. <label for="txt_search_departmentname" class="col-sm-4 control-label" style="margin-top:6px;">部门名称</label>
  13. <span class="col-sm-8">
  14. <input type="text" class="form-control" id="txt_search_departmentname">
  15. </span>
  16. </div>
  17. <div class="col-md-4">
  18. <label for="txt_search_statu" class="col-sm-3 control-label" style="margin-top:6px;">状态</label>
  19. <span class="col-sm-8">
  20. <input type="text" class="form-control" id="txt_search_statu">
  21. </span>
  22. </div>
  23. <div class="col-md-4">
  24. <button type="button" id="btn_query" class="btn btn-primary">查询</button>
  25. </div>
  26. </div>
  27. </div>
  28. </div>
  29. </div>
  30. <div id="toolbar" class="btn-group">
  31. <button id="btn_add" type="button" class="btn btn-default">
  32. <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>新增
  33. </button>
  34. <button id="btn_edit" type="button" class="btn btn-default">
  35. <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>修改
  36. </button>
  37. <button id="btn_delete" type="button" class="btn btn-default">
  38. <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>删除
  39. </button>
  40. </div>
  41. <table id="tb_departments"></table>
  42. <form>
  43. <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
  44. <div class="modal-dialog" role="document">
  45. <div class="modal-content">
  46. <div class="modal-header">
  47. <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
  48. <h4 class="modal-title" id="myModalLabel">新增</h4>
  49. </div>
  50. <div class="modal-body">
  51. <div class="form-group">
  52. <label for="txt_departmentname">部门名称</label>
  53. <input type="text" name="txt_departmentname" class="form-control" id="txt_departmentname" placeholder="部门名称">
  54. </div>
  55. <div class="form-group">
  56. <label for="txt_parentdepartment">上级部门</label>
  57. <input type="text" name="txt_parentdepartment" class="form-control" id="txt_parentdepartment" placeholder="上级部门">
  58. </div>
  59. <div class="form-group">
  60. <label for="txt_departmentlevel">部门级别</label>
  61. <input type="text" name="txt_departmentlevel" class="form-control" id="txt_departmentlevel" placeholder="部门级别">
  62. </div>
  63. <div class="form-group">
  64. <label for="txt_statu">状态</label>
  65. <input type="text" name="txt_statu" class="form-control" id="txt_statu" placeholder="状态">
  66. </div>
  67. </div>
  68. <div class="modal-footer">
  69. <button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span>关闭</button>
  70. <button type="button" id="btn_submit" class="btn btn-primary" data-dismiss="modal"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>保存</button>
  71. </div>
  72. </div>
  73. </div>
  74. </div>
  75. </form>
  76. Department.cshtml

JS代码我们来看一个页面就好了,其他页面类似:

  1. $(function () {
  2. $('#tb_departments').bootstrapTable({
  3. url: '/PowerManager/GetDepartments',
  4. method: 'post',
  5. toolbar: '#toolbar',
  6. pagination: true,
  7. queryParams: queryParams,
  8. queryParamsType: "limit",
  9. //ajaxOptions: { departmentname: "", statu: "" },
  10. sidePagination: "server",
  11. pageSize: 5,
  12. pageList: [5, 25, 50, 100],
  13. search: true,
  14. strictSearch: true,
  15. showColumns: true,
  16. showRefresh: true,
  17. minimumCountColumns: 2,
  18. clickToSelect: true,
  19. columns: [{
  20. checkbox: true
  21. }, {
  22. field: 'DEPARTMENT_NAME',
  23. title: '部门名称'
  24. }, {
  25. field: 'PARENT_ID',
  26. title: '上级部门'
  27. }, {
  28. field: 'DEPARTMENT_LEVEL',
  29. title: '部门级别'
  30. }, {
  31. field: 'STATUS',
  32. title: '状态'
  33. }, ],
  34. onLoadSuccess: function (data) {
  35. var odata = data;
  36. }
  37. });
  38. var oButtonInit = new ButtonInit();
  39. oButtonInit.Init();
  40. });
  41. function queryParams(params) { //配置参数
  42. var temp = { //这里的键的名字和控制器的变量名必须一直,这边改动,控制器也需要改成一样的
  43. limit: params.limit, //页面大小
  44. offset: params.offset, //页码
  45. departmentname: $("#txt_search_departmentname").val(),
  46. statu: $("#txt_search_statu").val()
  47. };
  48. return temp;
  49. }
  50. var ButtonInit = function () {
  51. var oInit = new Object();
  52. var postdata = {};
  53. oInit.Init = function () {
  54. $("#btn_add").click(function () {
  55. $("#myModalLabel").text("新增");
  56. $("#myModal").find(".form-control").val("");
  57. $('#myModal').modal()
  58. postdata.DEPARTMENT_ID = "";
  59. });
  60. $("#btn_edit").click(function () {
  61. var arrselections = $("#tb_departments").bootstrapTable('getSelections');
  62. if (arrselections.length > 1) {
  63. //alert("只能选择一行进行编辑");
  64. $("#btn_alert").alert();
  65. return;
  66. }
  67. if (arrselections.length <= 0) {
  68. //alert("请先选择需要编辑的行");
  69. $("#btn_alert").alert()
  70. return;
  71. }
  72. $("#myModalLabel").text("编辑");
  73. $("#txt_departmentname").val(arrselections[0].DEPARTMENT_NAME);
  74. $("#txt_parentdepartment").val(arrselections[0].PARENT_ID);
  75. $("#txt_departmentlevel").val(arrselections[0].DEPARTMENT_LEVEL);
  76. $("#txt_statu").val(arrselections[0].STATUS);
  77. postdata.DEPARTMENT_ID = arrselections[0].DEPARTMENT_ID;
  78. $('#myModal').modal();
  79. });
  80. $("#btn_delete").click(function () {
  81. var arrselections = $("#tb_departments").bootstrapTable('getSelections');
  82. if (arrselections.length <= 0) {
  83. //alert("请先选择需要编辑的行");
  84. $("#btn_alert").alert()
  85. return;
  86. }
  87. if (!confirm("确定要删除选定的数据吗?")) {
  88. return;
  89. }
  90. $.ajax({
  91. type: "post",
  92. url: "/PowerManager/DeleteDept",
  93. data: { strID: arrselections[0].DEPARTMENT_ID },
  94. success: function (data, status) {
  95. if (status == "success") {
  96. alert("提交数据成功");
  97. $("#tb_departments").bootstrapTable('refresh');
  98. }
  99. },
  100. error: function () {
  101. alert("error");
  102. },
  103. complete: function () {
  104. //alert("complete");
  105. }
  106. });
  107. });
  108. $("#btn_submit").click(function () {
  109. postdata.DEPARTMENT_NAME = $("#txt_departmentname").val();
  110. postdata.PARENT_ID = $("#txt_parentdepartment").val();
  111. postdata.DEPARTMENT_LEVEL = $("#txt_departmentlevel").val();
  112. postdata.STATUS = $("#txt_statu").val();
  113. $.ajax({
  114. type: "post",
  115. url: "/PowerManager/GetDepartmentEdit",
  116. data: { strPostData: JSON.stringify(postdata) },
  117. success: function (data, status) {
  118. if (status == "success") {
  119. alert("提交数据成功");
  120. $("#tb_departments").bootstrapTable('refresh');
  121. }
  122. },
  123. error: function () {
  124. //alert("error");
  125. },
  126. complete: function () {
  127. //alert("complete");
  128. }
  129. });
  130. });
  131. $("#btn_query").click(function () {
  132. $("#tb_departments").bootstrapTable('refresh');
  133. });
  134. };
  135. return oInit;
  136. };
  137. DepartmentManage.js

效果图:
DDD领域驱动设计初探(七):Web层的搭建 - 图4
DDD领域驱动设计初探(七):Web层的搭建 - 图5
DDD领域驱动设计初探(七):Web层的搭建 - 图6
在做页面数据更新的时候,博主又遇到一个问题:ObjectStateManager 中已存在具有同一键的对象。ObjectStateManager 无法跟踪具有相同键的多个对象。在此还是记录下解决方案:
在仓储的公共实现类中将

  1. public virtual IQueryable<TEntity> Entities
  2. {
  3. get { return UnitOfWork.context.Set<TEntity>(); }
  4. }

改成

  1. public virtual IQueryable<TEntity> Entities
  2. {
  3. get { return UnitOfWork.context.Set<TEntity>().AsNoTracking() as IQueryable<TEntity>; }
  4. }

就可以了。
至此,从领域模型到Web前端的代码基本完成,可能很多代码并未完善,比如异常处理、数据验证等。之前写过一篇CS版本的权限系统 系统设计——权限系统,很多朋友找我要过源码,那个时候可能代码都在工作的项目中,没办法抽离出来,在此表示抱歉。现在做了一个BS的,感觉BS比CS界面好看,在这里将源码分享出来,当然这里的代码肯定也不太全,很多没实现的功能还需要自己去实现,但是基本的架子搭起来了,有兴趣可以看看。源码下载


  • 本文作者:GeekPower - Felix Sun
  • 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!