新思想、新技术、新架构——更好更快的开发现代ASP.NET应用程序
Tdf团队开发框架分层架构图
Tdf团队开发框架汇集了一些比较流行的技术和开源项目,也把自己的程序架构、部分代码风格、前端表现简单做了一些展示。
由于这个功能实在太简单,没有使用到领域服务、领域事件,这里可能只能说明一件事件:没有复杂业务逻辑的功能使用此DDD框架,并不会增加代码量,反而我认为这样的代码量差不多已经少到极致了。
1 Tdf.Domain.Entities
Role.cs
using System;
using Tdf.Domain.Entities;
using Tdf.Domain.Entities.Auditing;
namespace Tdf.Domain.Act.Entities
{
public class Role : Entity
, IFullAudited<Guid?>
, IMustHaveTenant
{
public Guid TenantId { get; set; }
public string Name { get; set; }
public string DisplayName { get; set; }
public bool IsStatic { get; set; }
public bool IsDefault { get; set; }
public bool IsDeleted { get; set; }
public Guid? DeleterUserId { get; set; }
public DateTime? DeletionTime { get; set; }
public Guid? LastModifierUserId { get; set; }
public DateTime? LastModificationTime { get; set; }
public Guid? CreatorUserId { get; set; }
public DateTime CreationTime { get; set; }
public int DisOrder { get; set; }
public int State { get; set; }
}
}
2 Tdf.EntityFramework
ActDbContext.cs
public virtual IDbSet<Role> Roles { get; set; }
modelBuilder.Configurations.Add(new RoleMap());
RoleMap.cs
using System.Data.Entity.ModelConfiguration;
using Tdf.Domain.Act.Entities;
namespace Tdf.EntityFramework.Act.Mapping
{
public class RoleMap : EntityTypeConfiguration<Role>
{
public RoleMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Table & Column Mappings
this.ToTable("Act_Role");
this.Property(t => t.Id).HasColumnName("RoleId");
}
}
}
3 Tdf.Application
Dtos
CreateRoleInput.cs
using System;
using Tdf.Application.Services.Dto;
namespace Tdf.Application.Act.RoleMgr.Dtos
{
public class CreateRoleInput : IInputDto
{
public Guid TenantId { get; set; }
public string Name { get; set; }
public string DisplayName { get; set; }
public bool IsStatic { get; set; }
public bool IsDefault { get; set; }
public bool IsDeleted { get; set; }
public Guid? DeleterUserId { get; set; }
public DateTime? DeletionTime { get; set; }
public Guid? LastModifierUserId { get; set; }
public DateTime? LastModificationTime { get; set; }
public Guid? CreatorUserId { get; set; }
public DateTime CreationTime { get; set; }
public int DisOrder { get; set; }
public int State { get; set; }
}
}
UpdateRoleInput.cs
using System;
namespace Tdf.Application.Act.RoleMgr.Dtos
{
public class UpdateRoleInput : CreateRoleInput
{
public Guid Id { get; set; }
}
}
RoleDto.cs
using System;
namespace Tdf.Application.Act.RoleMgr.Dtos
{
public class RoleDto
{
public Guid Id { get; set; }
public Guid TenantId { get; set; }
public string Name { get; set; }
public string DisplayName { get; set; }
public bool IsStatic { get; set; }
public bool IsDefault { get; set; }
public bool IsDeleted { get; set; }
public Guid? DeleterUserId { get; set; }
public DateTime? DeletionTime { get; set; }
public Guid? LastModifierUserId { get; set; }
public DateTime? LastModificationTime { get; set; }
public Guid? CreatorUserId { get; set; }
public DateTime CreationTime { get; set; }
public string DisOrder { get; set; }
public string State { get; set; }
}
}
IRoleAppService.cs
using System.Threading.Tasks;
using Tdf.Application.Act.RoleMgr.Dtos;
using Tdf.Application.Services;
using Tdf.Application.Services.Dto;
namespace Tdf.Application.Act.RoleMgr
{
public interface IRoleAppService : IApplicationService
{
Task<JsonMessage> Create(CreateRoleInput model);
Task<JsonMessage> Update(UpdateRoleInput model);
Task<JsonMessage> GetInfo(DetailInfoInput model);
Task<JsonMessage> BatchDelete(BatchDeleteInput model);
JsonMessage GetPage(PageInput model);
}
}
RoleAppService
using AutoMapper;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Tdf.Application.Act.RoleMgr.Dtos;
using Tdf.Application.Services.Dto;
using Tdf.Domain.Act.Entities;
using Tdf.Domain.Repositories;
using Tdf.Domain.Uow;
using Tdf.Utils.Excp;
using Tdf.Utils.Networking;
namespace Tdf.Application.Act.RoleMgr
{
public class RoleAppService : IRoleAppService
{
private readonly IUnitOfWork _uow;
private readonly IRepository<Role> _roleRepository;
public RoleAppService(IUnitOfWork uow, IRepository<Role> roleRepository)
{
this._roleRepository = roleRepository;
this._uow = uow;
}
public async Task<JsonMessage> Create(CreateRoleInput model)
{
var entity = Mapper.Map<CreateRoleInput, Role>(model);
entity.Id = Guid.NewGuid();
await _roleRepository.InsertAsync(entity);
await _uow.SaveChangesAsync();
return ServiceResult.GetErrorMsgByErrCode(0);
}
public async Task<JsonMessage> Update(UpdateRoleInput model)
{
Role entity = await _roleRepository.SingleAsync(b => b.Id == model.Id);
entity.Name = model.Name;
entity.DisplayName = model.DisplayName;
entity.State = model.State;
entity.DisOrder = model.DisOrder;
await _roleRepository.UpdateAsync(entity);
await _uow.SaveChangesAsync();
return ServiceResult.GetErrorMsgByErrCode(0);
}
public async Task<JsonMessage> GetInfo(DetailInfoInput model)
{
var entity = await _roleRepository.SingleAsync(b => b.Id == model.Id);
var jsonMsg = ServiceResult.GetErrorMsgByErrCode(0);
jsonMsg.Result = Mapper.Map<Role, RoleDto>(entity);
return jsonMsg;
}
public async Task<JsonMessage> BatchDelete(BatchDeleteInput model)
{
if (model.IdList.Count == 0)
throw new CustomException(-1, "没有删除的对象!");
Role entity;
foreach (var item in model.IdList)
{
entity = await _roleRepository.SingleAsync(b => b.Id == item);
await _roleRepository.DeleteAsync(entity);
}
var cnt = await _uow.SaveChangesAsync();
var jsonMsg = ServiceResult.GetErrorMsgByErrCode(0);
jsonMsg.Result = $"成功删除{cnt}条数据";
return jsonMsg;
}
public JsonMessage GetPage(PageInput model)
{
if (string.IsNullOrEmpty(model.OrderBy))
model.OrderBy = "DisOrder";
object entity;
int total = 0;
if (!string.IsNullOrEmpty(model.Keyword))
{
entity = _roleRepository.Get(p => p.Name.Contains(model.Keyword), model.OrderBy, model.PageIndex, model.PageSize);
total = _roleRepository.Count(p => p.Name.Contains(model.Keyword));
}
else
{
entity = _roleRepository.Get(model.OrderBy, model.PageIndex, model.PageSize);
total = _roleRepository.Count();
}
var jsonMsg = ServiceResult.GetErrorMsgByErrCode(0);
jsonMsg.Result = new PageOutput() { Total = total, Records = Mapper.Map<List<RoleDto>>(entity) };
return jsonMsg;
}
}
}
4 Tdf.WebApi
** UnityConfig.cs **
container.RegisterType<ActDbContext>(new HierarchicalLifetimeManager());
container.RegisterType<IUnitOfWork, ActUnitOfWork>(new HierarchicalLifetimeManager());
container.RegisterType<IRepository<Role>, ActRepositoryBase<Role>>(new HierarchicalLifetimeManager());
container.RegisterType<IRoleAppService, RoleAppService>(new HierarchicalLifetimeManager());
** ActProfile.cs **
CreateMap<CreateRoleInput, Role>();
CreateMap<UpdateRoleInput, Role>();
CreateMap<Role, RoleDto>();
ActRoleController.cs
using System;
using System.Threading.Tasks;
using System.Web.Http;
using Tdf.Application.Act.RoleMgr;
using Tdf.Application.Act.RoleMgr.Dtos;
using Tdf.Application.Services.Dto;
namespace Tdf.WebApi.Controllers.ActMgr
{
public class ActRoleController : ApiController
{
private readonly IRoleAppService _roleAppService;
public ActRoleController(IRoleAppService roleAppService)
{
_roleAppService = roleAppService;
}
[Route("api/ActRole/CreateRole")]
[HttpPost]
public async Task<JsonMessage> CreateRole([FromBody]CreateRoleInput input)
{
string defaultGuid = "{41643A10-7D28-4692-94FA-4A427FE22147}";
input.TenantId = new Guid(defaultGuid);
input.IsStatic = true;
input.IsDefault = true;
input.IsDeleted = false;
input.CreatorUserId = Guid.NewGuid();
return await _roleAppService.Create(input);
}
[Route("api/ActRole/UpdateRole")]
[HttpPost]
public async Task<JsonMessage> UpdateRole([FromBody]UpdateRoleInput input)
{
return await _roleAppService.Update(input);
}
[Route("api/ActRole/GetPageRole")]
[HttpGet]
public JsonMessage GetPageRole([FromUri]PageInput input)
{
return _roleAppService.GetPage(input);
}
[Route("api/ActRole/GetRoleInfo")]
[HttpGet]
public async Task<JsonMessage> GetRoleInfo([FromUri]DetailInfoInput input)
{
return await _roleAppService.GetInfo(input);
}
[Route("api/ActRole/BatchDeleteRole")]
[HttpPost]
public async Task<JsonMessage> BatchDeleteRole([FromBody]BatchDeleteInput input)
{
return await _roleAppService.BatchDelete(input);
}
}
}
5 Tdf.Web
roleList.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="../../lib/jquery-easyui-1.4.1/themes/gray/easyui.css" rel="stylesheet" type="text/css" />
<link href="../../lib/jquery-easyui-1.4.1/themes/icon.css" rel="stylesheet" type="text/css" />
<link href="../../css/main.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="toolbar">
<div>
<a href="javascript:void(0)" class="easyui-linkbutton" iconcls="icon-add" onclick="add()" plain="true">新增</a>
<a href="javascript:void(0)" class="easyui-linkbutton" iconcls="icon-edit" onclick="edit()" plain="true">修改</a>
<a href="javascript:void(0)" class="easyui-linkbutton" iconcls="icon-cancel" onclick="del()" plain="true">删除</a>
<label style="margin-left:20px">关键字:</label><input id="txtKeyword" class="easyui-textbox" data-options="prompt:'请输入关键字'" style="width:150px;height:25px" value="" />
<a href="#" class="easyui-linkbutton" onclick="searchInfo()" iconCls="icon-search">查询</a>
</div>
</div>
<div id="dg">
</div>
<script src="../../js/jquery-1.10.2.min.js"></script>
<script src="../../lib/jquery-easyui-1.4.1/jquery.easyui.min.js"></script>
<script src="../../lib/jquery-easyui-1.4.1/locale/easyui-lang-zh_CN.js"></script>
<script src="../../js/boer_util.js"></script>
<script src="../../js/jQuery.extend.js"></script>
<script type="text/javascript">
var title = '角色管理'; // 1.dialogTitle
var dataKey = "Id"; // 2.dataKey
$(document).ready(function () {
var h = $(window).height();
$("#dg").datagrid({
title: title,
autoRowHeight: false,
loadMsg: "数据加载中,请稍候...",
pageList: [50, 40, 30, 20, 10],
pageSize: 20,
striped: true,
height: h,
fitColumns: true,
singleSelect: false,
rownumbers: true,
pagination: true,
nowrap: false,
showFooter: false,
toolbar: '#toolbar',
remotesort: true,
sortName: 'DisOrder', // 3.sortName
sortOrder: 'asc',
columns: [[ // 4.columns
{ field: dataKey, halign: 'center', checkbox: true },
{
field: 'Name', title: '角色名称', sortable: true, width: 200, halign: 'center', align: 'center', fixed: true,
formatter: function (value, row, index) {
return value;
}
},
{
field: 'DisplayName', title: '角色描述', sortable: true, width: 250, halign: 'center', align: 'center', fixed: true,
formatter: function (value, row, index) {
return value;
}
},
{
field: 'State', title: '使用状态', sortable: true, width: 100, halign: 'center', align: 'center', fixed: true,
formatter: function (value, row, index) {
switch (value) {
case '1': return "正在使用";
case '0': return "停止使用";
default: return value;
}
}
},
{
field: 'DisOrder', title: '显示顺序', sortable: true, width: 80, halign: 'center', align: 'center', fixed: true,
formatter: function (value, row, index) {
return value;
}
}
]],
onSortColumn: function (sort, order) {
loadData();
}
});
loadData();
});
function loadData() {
$('#dg').datagrid('getPager').pagination({
displayMsg: '当前显示从 [{from}] 到 [{to}] 共[{total}]条记录',
onSelectPage: function (pPageIndex, pPageSize) {
loadData();
}
});
var ajaxData = {};
var options = $('#dg').datagrid('getPager').data("pagination").options;
if (options.pageNumber < 1) {
options.pageNumber = 1;
}
var orderby = $('#dg').datagrid('options').sortName + ' ' + $('#dg').datagrid('options').sortOrder;
var keyword = $("#txtKeyword").textbox("getValue").trim();
ajaxData.PageIndex = options.pageNumber;
ajaxData.PageSize = options.pageSize;
ajaxData.OrderBy = orderby;
ajaxData.Keyword = keyword;
// 5.loadData request api
BoerAjax("/api/ActRole/GetPageRole", ajaxData, "get", function (data) {
if (data.ErrCode == 0) {
$('#dg').datagrid('clearSelections');
$('#dg').datagrid('loadData', GetEasyUIData(data.Result));
} else {
$.messager.alert('警告', '错误代码:' + data.ErrCode + ',' + data.ErrMsg, 'warning');
}
});
}
function add() {
top.$.formdialog({
id: "dlg_roleDetail",
title: "新增" + title,
src: VirtualAdminPath + "/act/roleDetail.html", // 6.add dialog path
width: 550,
height: 350,
iconCls: 'icon-edit',
handler: "formSubmit('dlg_roleDetail','" + title + "')"
});
top.$('#dlg_roleDetail').dialog("open");
}
function edit() {
var id = $.getSelected("dg", dataKey);
if (isEmpty(id)) {
$.messager.alert('警告', '请选择一项数据!');
return;
}
top.$.formdialog({
id: "dlg_roleDetail",
title: "修改" + title,
src: VirtualAdminPath + "/act/roleDetail.html?id=" + id, // 7.edit dialog path
width: 550,
height: 350,
iconCls: 'icon-edit',
handler: "formSubmit('dlg_roleDetail','" + title + "')"
});
top.$('#dlg_roleDetail').dialog("open");
}
function del() {
var ids = $.getSelections("dg", dataKey);
if (isEmpty(ids)) {
$.messager.alert('警告', '请至少选择一项!');
return;
}
$.messager.confirm('确定删除', '您确认想要删除这' + $('#dg').datagrid('getSelections').length + '条记录吗?', function (r) {
if (r) {
var ajaxData = {};
ajaxData.IdList = ids.split(',');
// 8.del request api
BoerAjax("/api/ActRole/BatchDeleteRole", ajaxData, "post", function (data) {
if (data.ErrCode == 0) {
$.messager.alert('成功', data.Result, 'info');
loadData();
} else {
$.messager.alert('警告', '错误代码:' + data.ErrCode + ',' + data.ErrMsg, 'warning');
}
});
};
});
}
function searchInfo() {
loadData();
}
function dataReload() {
$("#txtKeyword").val('');
searchInfo();
}
$(window).resize(function () {
onResize();
});
function onResize() {
$("#dg").height = $(window).height();
$("#dg").datagrid("options").width = $(window).width();
$("#dg").datagrid("resize");
}
</script>
</body>
</html>
roleDetail.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="../../css/formmain.css" rel="stylesheet" />
<link href="../../lib/jquery-easyui-1.4.1/themes/icon.css" rel="stylesheet" />
<link href="../../lib/jquery-easyui-1.4.1/themes/gray/easyui.css" rel="stylesheet" />
</head>
<body>
<div style="padding: 10px 10px 10px 10px">
<!-- 1.page columns -->
<table cellpadding="5">
<tr>
<td>角色名称:</td>
<td><input class="easyui-textbox" type="text" id="txtName" data-options="required:true" /></td>
</tr>
<tr>
<td>角色描述:</td>
<td><input class="easyui-textbox" data-options="multiline:true" style="width:300px;height:100px" type="text" id="txtDisplayName" /></td>
</tr>
<tr>
<td>使用状态:</td>
<td>
<select id="ddlState" class="easyui-combobox">
<option value="1">正在使用</option>
<option value="0">停止使用</option>
</select>
</td>
</tr>
<tr>
<td>显示顺序:</td>
<td><input class="easyui-textbox" type="text" id="txtDisOrder" data-options="required:true" /></td>
</tr>
</table>
</div>
<script src="../../js/jquery-1.10.2.min.js"></script>
<script src="../../lib/jquery-easyui-1.4.1/jquery.easyui.min.js" type="text/javascript"></script>
<script src="../../lib/jquery-easyui-1.4.1/locale/easyui-lang-zh_CN.js" type="text/javascript"></script>
<script src="../../js/boer_util.js"></script>
<script src="../../js/jQuery.extend.js"></script>
<script>
$(document).ready(function () {
var id = $.url("id");
if (!isEmpty(id)) {
loadInfo(id);
}
});
function loadInfo(id) {
// 2.loadInfo inDto parm
var ajaxData = {};
ajaxData.Id = id;
// 3.loadInfo request api
BoerAjax("/api/ActRole/GetRoleInfo", ajaxData, "get", function (data) {
if (data.ErrCode == 0) {
// 4.getInfo outDto to page
$("#txtName").textbox("setValue", data.Result.Name);
$("#txtDisplayName").textbox("setValue", data.Result.DisplayName);
$("#ddlState").combobox("setValue", data.Result.State);
$("#txtDisOrder").textbox("setValue", data.Result.DisOrder);
} else {
$.messager.alert('警告', '错误代码:' + data.ErrCode + ',' + data.ErrMsg, 'warning');
}
});
}
function formSubmit(obj, iframeName) {
var id = $.url("id");
// 5.page to inDto
var ajaxData = {};
ajaxData.Name = $("#txtName").textbox('getValue').trim();
ajaxData.DisplayName = $("#txtDisplayName").textbox('getValue').trim();
ajaxData.State = $("#ddlState").combobox('getValue').trim();
ajaxData.DisOrder = $("#txtDisOrder").textbox('getValue').trim();
if (ajaxData.Name == "") {
$.messager.alert('警告', '请输入角色名称!', 'warning');
return;
}
// 6.request create api
var url = "/api/ActRole/CreateRole";
if (!isEmpty(id)) {
// 7.request update api
url = "/api/ActRole/UpdateRole";
ajaxData.Id = id;
}
BoerAjax(url, ajaxData, "post", function (data) {
if (data.ErrCode == 0) {
top.window.frames[iframeName].dataReload();
top.$('#' + obj).dialog("close");
} else {
$.messager.alert('警告', '错误代码:' + data.ErrCode + ',' + data.ErrMsg, 'warning');
}
});
}
</script>
</body>
</html>