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

DDD领域驱动设计初探系列文章:

一、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服务业务接口代码

    /// <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 ;
}
//得到从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两个项目,如图:

为什么要有这么一个接口层?之前C#进阶系列——MEF实现设计上的“松耦合”(终结篇:面向接口编程)这篇已经做过介绍,对面向接口编程不了解的朋友可以看看。

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 == ""); //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 == "" && 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" content="width=device-width" />
<title>@ViewBag.Title</title>
@Styles.Render("~/Content/css")
@Styles.Render("~/Content/table-css")
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@Scripts.Render("~/bundles/bootstrap-table")
@RenderSection("Scripts", false)
</head>
<body>
@RenderBody()
</body>
</html>

_Layout.cshtml

@{
ViewBag.Title = "部门管理";
Layout = "~/Views/Shared/_Layout.cshtml";
} @Scripts.Render("~/bundles/PowerManage/DepartmentManage") <div class="panel-body" style="padding-bottom:0px;">
<div class="panel panel-default">
<div class="panel-heading">查询条件</div>
<div class="panel-body">
<div class="row">
<div class="col-md-4">
<label for="txt_search_departmentname" class="col-sm-4 control-label" style="margin-top:6px;">部门名称</label>
<span class="col-sm-8">
<input type="text" class="form-control" id="txt_search_departmentname">
</span>
</div>
<div class="col-md-4">
<label for="txt_search_statu" class="col-sm-3 control-label" style="margin-top:6px;">状态</label>
<span class="col-sm-8">
<input type="text" class="form-control" id="txt_search_statu">
</span>
</div> <div class="col-md-4">
<button type="button" id="btn_query" class="btn btn-primary">查询</button>
</div>
</div> </div>
</div>
</div> <div id="toolbar" class="btn-group">
<button id="btn_add" type="button" class="btn btn-default">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>新增
</button>
<button id="btn_edit" type="button" class="btn btn-default">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>修改
</button>
<button id="btn_delete" type="button" class="btn btn-default">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>删除
</button>
</div>
<table id="tb_departments"></table> <form>
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">新增</h4>
</div>
<div class="modal-body"> <div class="form-group">
<label for="txt_departmentname">部门名称</label>
<input type="text" name="txt_departmentname" class="form-control" id="txt_departmentname" placeholder="部门名称">
</div>
<div class="form-group">
<label for="txt_parentdepartment">上级部门</label>
<input type="text" name="txt_parentdepartment" class="form-control" id="txt_parentdepartment" placeholder="上级部门">
</div>
<div class="form-group">
<label for="txt_departmentlevel">部门级别</label>
<input type="text" name="txt_departmentlevel" class="form-control" id="txt_departmentlevel" placeholder="部门级别">
</div>
<div class="form-group">
<label for="txt_statu">状态</label>
<input type="text" name="txt_statu" class="form-control" id="txt_statu" placeholder="状态">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span>关闭</button>
<button type="button" id="btn_submit" class="btn btn-primary" data-dismiss="modal"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>保存</button>
</div>
</div>
</div>
</div>
</form>

Department.cshtml

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

$(function () {
$('#tb_departments').bootstrapTable({
url: '/PowerManager/GetDepartments',
method: 'post',
toolbar: '#toolbar',
pagination: true,
queryParams: queryParams,
queryParamsType: "limit",
//ajaxOptions: { departmentname: "", statu: "" },
sidePagination: "server",
pageSize: 5,
pageList: [5, 25, 50, 100],
search: true,
strictSearch: true,
showColumns: true,
showRefresh: true,
minimumCountColumns: 2,
clickToSelect: true,
columns: [{
checkbox: true
}, {
field: 'DEPARTMENT_NAME',
title: '部门名称'
}, {
field: 'PARENT_ID',
title: '上级部门'
}, {
field: 'DEPARTMENT_LEVEL',
title: '部门级别'
}, {
field: 'STATUS',
title: '状态'
}, ],
onLoadSuccess: function (data) {
var odata = data;
}
}); var oButtonInit = new ButtonInit();
oButtonInit.Init(); }); function queryParams(params) { //配置参数
var temp = { //这里的键的名字和控制器的变量名必须一直,这边改动,控制器也需要改成一样的
limit: params.limit, //页面大小
offset: params.offset, //页码
departmentname: $("#txt_search_departmentname").val(),
statu: $("#txt_search_statu").val()
};
return temp;
} var ButtonInit = function () {
var oInit = new Object();
var postdata = {}; oInit.Init = function () {
$("#btn_add").click(function () {
$("#myModalLabel").text("新增");
$("#myModal").find(".form-control").val("");
$('#myModal').modal() postdata.DEPARTMENT_ID = "";
}); $("#btn_edit").click(function () {
var arrselections = $("#tb_departments").bootstrapTable('getSelections');
if (arrselections.length > 1) {
//alert("只能选择一行进行编辑");
$("#btn_alert").alert();
return;
}
if (arrselections.length <= 0) {
//alert("请先选择需要编辑的行");
$("#btn_alert").alert()
return;
}
$("#myModalLabel").text("编辑");
$("#txt_departmentname").val(arrselections[0].DEPARTMENT_NAME);
$("#txt_parentdepartment").val(arrselections[0].PARENT_ID);
$("#txt_departmentlevel").val(arrselections[0].DEPARTMENT_LEVEL);
$("#txt_statu").val(arrselections[0].STATUS); postdata.DEPARTMENT_ID = arrselections[0].DEPARTMENT_ID;
$('#myModal').modal();
}); $("#btn_delete").click(function () {
var arrselections = $("#tb_departments").bootstrapTable('getSelections');
if (arrselections.length <= 0) {
//alert("请先选择需要编辑的行");
$("#btn_alert").alert()
return;
} if (!confirm("确定要删除选定的数据吗?")) {
return;
} $.ajax({
type: "post",
url: "/PowerManager/DeleteDept",
data: { strID: arrselections[0].DEPARTMENT_ID },
success: function (data, status) {
if (status == "success") {
alert("提交数据成功");
$("#tb_departments").bootstrapTable('refresh');
}
},
error: function () {
alert("error");
},
complete: function () {
//alert("complete");
} });
}); $("#btn_submit").click(function () {
postdata.DEPARTMENT_NAME = $("#txt_departmentname").val();
postdata.PARENT_ID = $("#txt_parentdepartment").val();
postdata.DEPARTMENT_LEVEL = $("#txt_departmentlevel").val();
postdata.STATUS = $("#txt_statu").val();
$.ajax({
type: "post",
url: "/PowerManager/GetDepartmentEdit",
data: { strPostData: JSON.stringify(postdata) },
success: function (data, status) {
if (status == "success") {
alert("提交数据成功");
$("#tb_departments").bootstrapTable('refresh');
}
},
error: function () {
//alert("error");
},
complete: function () {
//alert("complete");
} });
}); $("#btn_query").click(function () {
$("#tb_departments").bootstrapTable('refresh');
});
}; return oInit;
};

DepartmentManage.js

效果图:

在做页面数据更新的时候,博主又遇到一个问题:ObjectStateManager 中已存在具有同一键的对象。ObjectStateManager 无法跟踪具有相同键的多个对象。在此还是记录下解决方案:

在仓储的公共实现类中将

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

改成

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

就可以了。

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

C#进阶系列——DDD领域驱动设计初探(七):Web层的搭建的更多相关文章

  1. C#进阶系列——DDD领域驱动设计初探(一):聚合

    前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的 ...

  2. C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上)

    前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原 ...

  3. C#进阶系列——DDD领域驱动设计初探(三):仓储Repository(下)

    前言:上篇介绍了下仓储的代码架构示例以及简单分析了仓储了使用优势.本章还是继续来完善下仓储的设计.上章说了,仓储的最主要作用的分离领域层和具体的技术架构,使得领域层更加专注领域逻辑.那么涉及到具体的实 ...

  4. C#进阶系列——DDD领域驱动设计初探(四):WCF搭建

    前言:前面三篇分享了下DDD里面的两个主要特性:聚合和仓储.领域层的搭建基本完成,当然还涉及到领域事件和领域服务的部分,后面再项目搭建的过程中慢慢引入,博主的思路是先将整个架构走通,然后一步一步来添加 ...

  5. C#进阶系列——DDD领域驱动设计初探(五):AutoMapper使用

    前言:前篇搭建了下WCF的代码,就提到了DTO的概念,对于为什么要有这么一个DTO的对象,上章可能对于这点不太详尽,在此不厌其烦再来提提它的作用: 从安全上面考虑,领域Model都带有领域业务,让Cl ...

  6. C#进阶系列——DDD领域驱动设计初探(六):领域服务

    前言:之前一直在搭建项目架构的代码,有点偏离我们的主题(DDD)了,这篇我们继续来聊聊DDD里面另一个比较重要的知识点:领域服务.关于领域服务的使用,书中也介绍得比较晦涩,在此就根据博主自己的理解谈谈 ...

  7. DDD领域驱动设计初探(七):Web层的搭建

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

  8. DDD领域驱动设计初探(六):领域服务

    前言:之前一直在搭建项目架构的代码,有点偏离我们的主题(DDD)了,这篇我们继续来聊聊DDD里面另一个比较重要的知识点:领域服务.关于领域服务的使用,书中也介绍得比较晦涩,在此就根据博主自己的理解谈谈 ...

  9. DDD领域驱动设计初探(五):AutoMapper使用

    前言:前篇搭建了下WCF的代码,就提到了DTO的概念,对于为什么要有这么一个DTO的对象,上章可能对于这点不太详尽,在此不厌其烦再来提提它的作用: 从安全上面考虑,领域Model都带有领域业务,让Cl ...

随机推荐

  1. jQuery实现鼠标经过图片变亮效果

    在线体验效果:http://hovertree.com/texiao/jquery/1.htm 以下是完整源代码: <!DOCTYPE html PUBLIC "-//W3C//DTD ...

  2. 超简洁的CSS下拉菜单

    效果体验:http://hovertree.com/texiao/css/3.htm HTML文件代码: <!DOCTYPE html> <html xmlns="http ...

  3. html5+Canvas实现酷炫的小游戏

    最近除了做业务,也在尝试学习h5和移动端,在这个过程中,学到了很多,利用h5和canvas做了一个爱心鱼的小游戏.点这里去玩一下 PS: 貌似有点闪屏,亲测多刷新两下就好了==.代码在本地跑都不会闪, ...

  4. linux下安装MySQL出错file /usr/share/mysql/charsets/latin2.xml from install of MySQL-......

    linux64 red hat 6.3企业版 开始错误装了32位mysql最后怎么也装不上64mysql了,折腾了一番最后全部卸载搞定: file /etc/init.d/mysql from ins ...

  5. 二次、三次贝塞尔曲线demo(演示+获取坐标点)

    二次贝塞尔曲线demo: See the Pen quadraticCurveDemo by hanyanjun (@hanyanjun) on CodePen. 我的demo地址(二次) 推荐点击以 ...

  6. Atitit.常见软件 数据 交换格式 标准

    Atitit.常见软件 数据 交换格式 标准 1. 常见的数据格式txt ,doc ,pic,music ,vodio1 2. 通用格式json yaml phpstr1 3. 专用格式1 4. 用户 ...

  7. Event 8306 5021 5059 5057发布安全令牌时异常

    现象:站点访问一个,应用程序池就自动停掉一个 日志报错       解决: 1.受域策略影响,作为批处理作业登录选项被锁定,需修改域策略: 更新后:     2.确保程序池账户倒在IIS_IUSER组 ...

  8. 【原】设置iOS项目BuildVersion自动增加

    一.概念阐述:Build与Version的区别 在iOS中有两种“版本号”,也就是所谓的version号与build号,如下图所示: 我们用最简洁的语言来区分这两个版本号的区别以及用途如下: Vers ...

  9. 从AdventureWorks学习数据库建模——国际化

    前一篇博客我已经把各个实体分析了一遍,从分析中可以看到,这个公司是做本地采购,生产,然后通过网站和门店进行国际销售的.所以这里会涉及到一些国际化的问题.接下来就来分析一下有哪些国际化需要注意的问题和数 ...

  10. [python]爬虫学习(一)

    要学习Python爬虫,我们要学习的共有以下几点(python2): Python基础知识 Python中urllib和urllib2库的用法 Python正则表达式 Python爬虫框架Scrapy ...