ABP框架实战 1.基础信息维护
在之前的一个开发项目中,因为公司战略发展,引用了这个ABP开源框架作为新项目的基础版本,由于客户的要求需要迁移旧系统数据,以及其他的一些原因,数据库采用了Oracle数据库管理。所以引用了Dapper这个ORM框架作为数据访问的扩展,在前期的开发过程中,碰到过很多的坑,但是通过学习阳光铭睿,tkb至简这两位博主写的一系列文章,以及其他同事的帮助下,慢慢的熟悉了ABP框架的一些原理以及思路。
下面简单介绍一下这个Abp框架以及一些学习的资料:
ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称。
ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应用程序的新起点,它旨在成为一个通用的WEB应用程序框架和项目模板。
ASP.NET Boilerplate 基于DDD的经典分层架构思想,实现了众多DDD的概念(但没有实现所有DDD的概念)。
ABP的官方网站:http://www.aspnetboilerplate.com
ABP在Github上的开源项目:https://github.com/aspnetboilerplate
阳光铭睿:http://www.cnblogs.com/mienreal/p/4528470.html
tkb至简:http://www.cnblogs.com/farb/p/ABPTheory.html
1.在历经了7个月(1607-1701)的时间,项目前两期的开发工作也算是全部的完成了,幸运的我一直留到了最后,负责后续功能以及App对接的开发,以及试运行问题过程中客户提出问题的维护。
2.在后面的某个日期中,全体这个项目的参与人员进行了一次会议总结,总体来说还算不错吧。
3.在后来的某一天,自己想要在最基础的框架上面弄一个基础的框架管理,关于简单的用户-部门-角色-权限管理这一块,可能还有很多地方没有弄完善的地方,目前还存在一部分的,但是简单来说也能满足一些简单的管理需求吧。(因为在这个项目开发过程中,这一块的数据管理是用的之前公司成熟的一套PMI框架,引用的单点登录,所以只能够参照他的思路来弄)
项目总体框架:
这里链接一个同事写的一篇文章吧,上面有关于这个项目框架的一些详细介绍http://www.cnblogs.com/yuanbeier/articles/6394484.html
下面讲一下自己弄这个的开发过程吧。
a.建立PDM,数据对应关系
b.生成数据库,以及实体,其实这里在ABP最初始的做法是采用Code First模式先建立实体在生成数据结构,个人觉得看自己习惯吧。
c.创立对应的Application Services, Controller,View,Js
d.然后就是具体的编码业务完成了,这个前段采用最快速的easyui开发,在这里参考了之前一个同事写的一个基类,然后在其思路上重新了写了一个基类,在这里的思路也正是考虑到ABP的约定大于规范的开发思路
1 var TopeveryBase = Base.extend({ 2 /*构造方法*/ 3 constructor: function () { 4 }, 5 /*初始化表格*/ 6 initGrid: function (options) { 7 options = $.extend({}, { id: "grid", width: "100%", height: "600", method: "post", singleSelect: true }, options || {}); 8 if (options.url == null) { 9 options.url = $("#" + options.id).attr("url"); 10 } 11 if (typeof (options.url) == "undefined") { 12 options.url = $("#" + options.id).attr("turl"); 13 } 14 if ($("#" + options.id).attr("singleSelect") === "false") { 15 options.singleSelect = true; 16 } 17 var gridParm = { 18 idField: "id", 19 fitColumns: true, 20 loadMsg: false, 21 nowrap: false, 22 queryParams: options.queryParams, 23 method: options.method, 24 singleSelect: options.singleSelect, 25 iconCls: "icon-save", 26 striped: true, 27 height: options.height, 28 animate: true, 29 collapsible: true, 30 border: true, 31 rownumbers: true, //行号 32 pagination: options.pagination || true, //分页控件 33 pageSize: 10, 34 pageList: [1, 10, 20, 50, 100, 250, 500, 1000], 35 sortName: "Id", 36 sortOrder: "desc", 37 toolbar: '#toolbarWrap', 38 onLoadSuccess: function (data) { 39 $(this).datagrid('doCellTip', { 'max-width': '400px', 'delay': 500 }); 40 $(this).datagrid("clearSelections").datagrid("clearChecked"); 41 }, 42 loader: function (param, success, error) { 43 if (typeof (param.sort) == "undefined") { 44 param.sort = "Id"; 45 } 46 if (typeof (param.order) == "undefined") { 47 param.order = "desc"; 48 } 49 var postParm = { 50 PageIndex: param.page, 51 PageCount: param.rows, 52 sort: param.sort, 53 order: param.order 54 }; 55 if (options.queryParams != null) { 56 postParm = $.extend({}, postParm, options.queryParams); 57 } 58 postParm = $.extend({}, postParm); 59 var formpostdata = topevery.form2Json("selectFrom"); 60 postParm = $.extend({}, postParm, formpostdata); 61 /* 获取详情*/ 62 easyuiBase.ajax({ 63 type: "post", 64 url: options.url, 65 data: JSON.stringify(postParm), 66 loading: false 67 }, function (data) { 68 if (data.Success) { 69 var array = new Object(); 70 array.rows = data.Result.Rows; 71 array.total = data.Result.Total; 72 success(array); 73 } else { 74 error(); 75 } 76 }, true); 77 } 78 }; 79 for (var i = 0; i < $(".easyui-textbox").length; i++) { 80 $("#" + $('.easyui-textbox').eq(i)[0].id + "").textbox({ 81 inputEvents: $.extend({}, $.fn.textbox.defaults.inputEvents, { 82 keyup: function (event) { 83 if (event.keyCode === 13) { 84 User.loadInfo(); 85 } 86 } 87 }) 88 }); 89 }; 90 if (options.columns != null) { 91 gridParm.columns = options.columns; 92 } 93 $("#" + options.id).datagrid(gridParm); 94 }, 95 /** 96 * 97 * @param {} id 修改时所用查询编号 98 * @param {} 加载修改查看 callback 回调函数 99 * @param {} 新增修改之后 callback1 回调函数 100 * @param {} options 参数集合 101 * @returns {} 102 */ 103 View: function (id, callback, callback1, options) { 104 if (id > 0) { 105 options = $.extend({}, { id: "edit", btn: "save", sumbit: "sumbitForm", grid: "grid" }, options || {}); 106 } else { 107 options = $.extend({}, { id: "add", btn: "save", sumbit: "sumbitForm", grid: "grid" }, options || {}); 108 } 109 var urlstring = $("#" + options.id).attr("url").split(','); 110 //geturl 获取修改需要加载信息 111 if (options.geturl == null) { 112 if (urlstring.length > 0) { 113 options.geturl = urlstring[1]; 114 } 115 } 116 ///新增or修改url 117 if (options.url == null) { 118 options.url = urlstring[0]; 119 } 120 $("#" + options.sumbit).form("reset"); 121 if (id !== "undefined" && id != null) { 122 //修改时 数据加载到页面 123 easyuiBase.ajax({ 124 type: "POST", 125 url: options.geturl, 126 data: JSON.stringify({ Id: id }), 127 loading: false 128 }, function (data) { 129 if (data.Success) { 130 var row = data.Result; 131 $("#" + options.sumbit).form("load", row); 132 callback(row); 133 } else { 134 error(); 135 } 136 }); 137 } 138 //弹出层dialog Id 139 if (options.name == null) { 140 options.name = $("#" + options.id).attr("name"); 141 } 142 $("#" + options.name).dialog('open'); 143 $("#" + options.btn).click(function () { 144 if ($("#" + options.sumbit).form('validate') === false) { 145 return; 146 } 147 easyuiBase.ajax({ 148 type: "post", 149 url: options.url, 150 data: JSON.stringify(easyuiBase.form2Json(options.sumbit)), 151 loading: false 152 }, function (data) { 153 if (data.Success) { 154 if (data.Result.IsSuccess) { 155 $("#" + options.name).dialog('close'); 156 try { 157 topeveryMessage.show(data.Result.Message); 158 } catch (e) { 159 } 160 $("#" + options.sumbit).form("reset"); 161 $("#" + options.grid).datagrid("load"); 162 $("#" + options.btn).unbind(); 163 if (callback1 !== "undefined" && callback1 != null) { 164 callback1(data.Result); 165 } 166 } else { 167 try { 168 topeveryMessage.show(data.Result.Message); 169 } catch (e) { 170 171 } 172 } 173 } else { 174 error(); 175 } 176 }); 177 }); 178 }, 179 //删除 180 delData: function (options, callback) { 181 options = $.extend({}, { id: "delete", grid: "grid" }, options || {}); 182 var urlstring = $("#" + options.id).attr("url"); 183 ///删除url 184 if (options.url == null) { 185 options.url = urlstring; 186 } 187 var arrRows = $('#' + options.grid).datagrid('getSelections'); 188 if (arrRows.length === 0) { 189 $.messager.alert('提示', '请选择一条需要删除的记录!', 'info'); 190 } else { 191 var ids = []; 192 $.each(arrRows, function () { 193 ids.push(this.Id); 194 }); 195 easyuiBase.ajax({ 196 type: "post", 197 url: options.url, 198 data: JSON.stringify({ "Ids": ids.join() }), 199 contentType: "application/json", 200 loading: false 201 }, function (data) { 202 if (data.Success) { 203 if (data.Result.IsSuccess) { 204 try { 205 $("#" + options.grid).datagrid('reload'); 206 } catch (e) { 207 208 } 209 callback(); 210 topeveryMessage.show(data.Result.Message); 211 } else { 212 try { 213 topeveryMessage.show(data.Result.Message); 214 } catch (e) { 215 216 } 217 } 218 } else { 219 error(); 220 } 221 }, true); 222 } 223 }, 224 ///搜索 225 loadInfo: function (options) { 226 options = $.extend({}, { grid: "grid" }, options || {}); 227 $('#' + options.grid).datagrid('load'); //点击搜索 228 }, 229 //清空 230 empty: function (options) { 231 options = $.extend({}, { sumbit: "selectFrom", grid: "grid", tree: "tree" }, options || {}); 232 $("#" + options.sumbit).form("reset"); 233 $("#" + options.grid).datagrid("load"); 234 $('#' + options.tree).find('.tree-node-selected').removeClass('tree-node-selected'); 235 } 236 });
1 var DirectoryManageList; 2 var base; 3 var load; 4 var defaultDeptId; 5 var tree; 6 $(function () { 7 DirectoryManageList = TopeveryBase.extend({ 8 }); 9 load = function (row) { 10 $("#LoginPassword1").textbox("setValue", row.LoginPassword); 11 } 12 base = new DirectoryManageList(); 13 var columns = [ 14 [ 15 { field: "Id", checkbox: true }, 16 { width: 100, title: '用户名', field: 'Name', align: 'center' }, 17 { width: 100, title: '登录名', field: 'LoginName', align: 'center' }, 18 { width: 100, title: '电话', field: 'TelNum', align: 'center' }, 19 { 20 width: 100, title: '默认部门', field: 'DefaultDeptName', align: 'center' 21 }, 22 { width: 100, title: '邮件', field: 'Email', align: 'center' }, 23 { width: 80, title: '手机', field: 'MobileNum', align: 'center' }, 24 { 25 title: '操作', 26 field: 'Action', 27 width: '15%', 28 align: 'center', 29 formatter: function (value, row, index) { 30 var c = ' <a href="#" class="easyui-modifyoperate" onclick="base.View(' + row.Id + ',load)">修改</a>'; 31 var d = ' <a href="#" class="easyui-modifyoperate" onclick="base.View(' + row.Id + ',load)">部门角色权限</a>'; 32 return c + " "+d; 33 } 34 } 35 ] 36 ]; 37 base.initGrid({ columns: columns }); 38 $('#DefaultDeptId').combotree({ 39 url: "/Dept/GetDeptList", 40 panelHeight: 'auto', 41 panelMaxHeight: 150, 42 loadFilter: function (data) { 43 var object = new Array; 44 object.push({ id: 0, text: " ", children: data, state: "0", attributes: "" }); 45 return object; 46 } 47 }), 48 $('#tree').tree({ 49 url: "/Dept/GetDeptList", 50 loadFilter: function (data) { 51 var object = new Array; 52 object.push({ id: 0, text: "部门树", children: data, state: "0", attributes: "" }); 53 return object; 54 }, 55 onClick: function (node) { //单击事件 56 defaultDeptId = node.id; 57 $("#defaultDeptId").val(defaultDeptId); 58 base.loadInfo(); 59 } 60 }); 61 });
@using System.Web.Optimization <div style="width: 10%; float: left; height: 100%; background-color: #fff;"> <ul id="tree"></ul> </div> <table style="width: 90%!important; float: left;" singleSelect="false" id="grid" url="UserR/GetListAsync"></table> <div id="toolbarWrap"> <div class="toolbar-area"> <div class="searching-area"> <form name="selectFrom" id="selectFrom"> <table> <tr> <td> <input class="easyui-textbox" style="width: 120px; height: 30px;" id="Name1" name="Name" data-options="prompt:'用户名'" /> <input class="easyui-textbox" style="width: 120px; height: 30px;" id="LoginName1" name="LoginName" data-options="prompt:'登录名'" /> <input id="defaultDeptId" name="defaultDeptId" style="display: none;" /> <a href="#" class="search-btn easyui-normalyellowbutton" onclick="base.loadInfo();">查询</a> <a href="#" class="search-btn easyui-normalyellowbutton" onclick="base.empty();">清空</a> </td> </tr> </table> </form> </div> </div> <div id="toolbar" class="toolbar operate-area-new"> <a href="#" onclick="base.View();" class="easyui-normalbluebutton">新增用户</a> <a href="#" onclick="base.delData();" class="easyui-normalbluebutton">删除用户</a> <input type="hidden" id="edit" name="NewModifView" url="UserW/UserAddEditAsync,UserR/GetOneAsync"> <input type="hidden" id="add" name="NewModifView" url="UserW/UserAddEditAsync"> <input type="hidden" id="delete" url="UserW/DeleteUserAsync"> </div> </div> <div class="easyui-dialog" title="新增" data-options="iconCls:'pag-list',modal:true,collapsible:false,minimizable:false,maximizable:false,resizable:false,closed:true" id="NewModifView" style="width: 700px; height: 380px; display: none;"> <form id="sumbitForm" name="sumbitForm"> <div class="house-lost-register-form"> <input id="Id" name="Id" style="display: none;" /> <p> <label>用户名:</label> <input type="text" class="easyui-textbox" maxlength="20" style="width: 215px; height: 30px;" id="Name" name="Name" required="required" missingMessage="用户名不能为空" /> <label>登录名:</label> <input type="text" class="easyui-textbox" style="width: 215px; height: 30px;" id="LoginName" name="LoginName" required="required" missingMessage="登录名不能为空" /> </p> <p> <label>登录密码:</label> <input type="text" class="easyui-textbox" style="width: 215px; height: 30px;" id="LoginPassword" name="LoginPassword" required="required" missingMessage="登录密码不能为空" /> <label>重复登录密码:</label> <input type="text" class="easyui-textbox" style="width: 215px; height: 30px;" id="LoginPassword1" name="LoginPassword1" required="required" missingMessage="重复登录密码不能为空" validType="equalTo['#LoginPassword']" invalidMessage="两次输入密码不匹配" /> </p> <p> <label>默认部门:</label> <input type="text" class="easyui-combotree" style="width: 215px; height: 30px;" id="DefaultDeptId" name="DefaultDeptId" /> <label>电话:</label> <input type="text" class="easyui-textbox" style="width: 215px; height: 30px;" id="TelNum" name="TelNum" /> </p> <p> <label>邮件:</label> <input type="text" class="easyui-textbox" style="width: 215px; height: 30px;" id="Email" name="Email" /> <label>手机:</label> <input type="text" class="easyui-textbox" style="width: 215px; height: 30px;" id="MobileNum" name="MobileNum" /> </p> <div style="padding-top: 20px; text-align: center;" id="ToView"> <a href="#" class="easyui-normalbluebutton" id="save">确认</a> </div> </div> </form> </div> @Scripts.Render("~/js/User/IndexTest.js")
如果是简单的增删改查,就能够通过简单的一个页面就可以完成了。
/// <summary> /// 去掉集合2中Id集合2中的数据 /// </summary> /// <typeparam name="TF"></typeparam> /// <returns></returns> public IQueryable<TF> NotShorthand<TF>(List<IdInput> list, IRepository<TF> iRepository) where TF : class, IEntity<int> { var idlist = new int[list.Count]; for (var i = 0; i < list.Count; i++) { idlist.SetValue(list[i].Id, i); } var query = from t in iRepository.GetAll() where !(idlist).Contains(t.Id) select t; return query; } /// <summary> /// 排序 分页封装 /// </summary> /// <typeparam name="T"></typeparam> /// <typeparam name="TF"></typeparam> /// <returns></returns> public async Task<PagedResultOutputDto<TF>> GetListAsync<T,TF>(IQueryable<T> query,int pageIndex,int pageCount,string sort) { query = !string.IsNullOrWhiteSpace(sort) ? query.OrderBy(sort) : query.OrderBy("Id desc"); var count = query.Count(); query = query.Skip((pageIndex - 1) * pageCount).Take(pageCount); var row = await query.ToListAsync(); var data = row.MapTo<List<TF>>(); return new PagedResultOutputDto<TF>(count, data); }
/// <summary> /// 根据部门Id获取可以调入的人员 /// </summary> /// <param name="input"></param> /// <returns></returns> public async Task<PagedResultOutputDto<UserListOutputDto>> GetNotUserAsync(DeptListInput input) { var query1 = (from userDept in _userDeptRepository.GetAll() join dept in _deptRepository.GetAll() on userDept.DeptId equals dept.Id where userDept.DeptId == input.DeptId select new IdInput { Id = userDept.UserId }).ToList(); var query = NotShorthand(query1, _userRepository); if (!string.IsNullOrWhiteSpace(input.Name)) { query = query.Where(x => x.Name.Contains(input.Name)); } var list = await GetListAsync<Zero.Core.Authorization.User, UserListOutputDto>(query, input.PageIndex, input.PageCount, input.Sorting); return list; }
4.最后总结是,也许我自己也不知道我的目的是什么,弄这个的意义是什么,但是做自己想做的,想到了就去做吧。
5.发现问题,然后思考问题,最后去解决这个问题!
百度云地址:http://pan.baidu.com/s/1c2cjpLm 提取码:4b66