extjs+MVC4+PetaPoco+AutoFac+AutoMapper后台管理系统(附源码)
前言
本项目使用的开发环境及技术列举如下:
1、开发环境
IDE:VS2010+MVC4
数据库:SQLServer2008
2、技术
前端:Extjs
后端:
(1)、数据持久层:轻量级ORM框架PetaPoco
(2)、依赖注入:AutoFac
(3)、对象关系映射:AutoMapper
(4)、数据验证(MVC自带的验证封装使用)
(5)、SQL翻译机
(6)、缓存
以上使用都参考或直接借鉴使用了园子内牛人们的代码,只是学习交流使用而已,还请勿怪,我为了简便,没有分多个类库,而是以文
件夹的形式分的,大家可以根据文件夹分成类库也是一样的。好了,废话不多说,还是先上几张图大家看看吧,如果有兴趣再往下看
项目截图
要点一:Extjs
本项目虽然功能不多,但是基本已经涵盖了extjs的所有基本用法了,对于一般的extjs初学者或是简单应用的开发应该是足够了,后
台开发主要在tab、grid、treegrid的用法比较多,也是比较麻烦的地方,下面直接上代码,大家看看
首页布局:
Grid行内增删改查:
1 Ext.onReady(function () { 2 // ExtJS组件自适应浏览器大小改变,看还有没有其他实现方式 3 Ext.EventManager.onWindowResize(function () { 4 Ext.ComponentManager.each(function (cmpId, cmp, length) { 5 if (cmp.hasOwnProperty("renderTo")) { 6 cmp.doLayout(); 7 } 8 }); 9 }); 10 var toolbar = Ext.create('Ext.toolbar.Toolbar', { 11 renderTo: document.body, 12 items: [ 13 // 使用右对齐容器 14 '->', // 等同 { xtype: 'tbfill' } 15 { 16 xtype: 'textfield', 17 name: 'roleName', 18 id: 'roleName', 19 emptyText: '输入角色名关键字', 20 listeners: { 21 specialkey: function (field, e) { 22 if (e.getKey() == Ext.EventObject.ENTER) { 23 store.load({ //传递查询条件参数 24 params: { 25 roleName: Ext.getCmp('roleName').getValue() 26 } 27 }); 28 } 29 } 30 } 31 }, 32 { 33 // xtype: 'button', // 默认的工具栏类型 34 text: '查询', 35 tooltip: '根据数据条件查询数据', 36 iconCls: "Zoom", 37 listeners: { 38 click: function () { 39 store.load({ //传递查询条件参数 40 params: { 41 roleName: Ext.getCmp('roleName').getValue() 42 } 43 }); 44 } 45 } 46 }, 47 // 添加工具栏项之间的垂直分隔条 48 '-', // 等同 {xtype: 'tbseparator'} 创建 Ext.toolbar.Separator 49 { 50 // xtype: 'button', // 默认的工具栏类型 51 text: '重置', 52 tooltip: '清空当前查询条件', 53 iconCls: "Arrowrotateanticlockwise", 54 handler: function () { //此事件可以代替click事件 55 Ext.getCmp('roleName').setValue(""); 56 } 57 }, 58 ] 59 }); 60 //1.定义Model 61 Ext.define("BeiDream.model.BeiDream_Role", { 62 extend: "Ext.data.Model", 63 fields: [ 64 { name: 'ID', type: 'int' }, 65 { name: 'Name', type: 'string' }, 66 { name: 'Description', type: 'string' }, 67 { name: 'IsUsed', type: 'boolean', defaultValue: true } 68 ] 69 }); 70 //2.创建store 71 var store = Ext.create("Ext.data.Store", { 72 model: "BeiDream.model.BeiDream_Role", 73 autoLoad: true, 74 pageSize: 10, 75 proxy: { 76 type: 'ajax', 77 api: { 78 read: RoleListUrl, //查询 79 create: AddUrl, //创建 80 update: UpdateUrl, //更新,必须真正修改了才会触发 81 destroy: RemoveUrl //删除 82 }, 83 reader: { 84 type: 'json', 85 root: 'data' 86 }, 87 writer: { 88 type: 'json', //默认格式 //MVC下后台使用模型自动进行转换,如果是普通webform,则配置root:'data',encode:'true',这样之后就可以使用request【data】获取 89 writeAllFields: true, //false只提交修改过的字段 90 allowSingle: false //默认为true,为true时,一条数据不以数组形式提交,为false时,都以数组形式提交,这样避免了提交了一条数据时,后台是list模型无法接收到数据问题 91 }, 92 listeners: { 93 exception: function (proxy, response, operation) { 94 grid.store.load(); //删除失败,数据重新加载 95 var resText = Ext.decode(response.responseText); 96 Ext.MessageBox.show({ 97 title: '服务器端异常', 98 msg: resText.msg, 99 icon: Ext.MessageBox.ERROR, 100 buttons: Ext.Msg.OK 101 }); 102 } 103 } 104 } 105 // sorters: [{ 106 // //排序字段。 107 // property: 'id' 108 // }] 109 }); 110 store.on('beforeload', function (store, options) { 111 var params = { roleName: Ext.getCmp('roleName').getValue() }; 112 Ext.apply(store.proxy.extraParams, params); 113 }); 114 var Gridtoolbar = Ext.create('Ext.toolbar.Toolbar', { 115 renderTo: document.body, 116 items: [{ 117 text: '新增', 118 tooltip: '新增一条数据', 119 iconCls: 'Add', 120 handler: function () { 121 RowEditing.cancelEdit(); 122 // Create a model instance 123 var r = new BeiDream.model.BeiDream_Role(); 124 Ext.getCmp('RoleGrid').getStore().insert(0, r); 125 RowEditing.startEdit(0, 0); 126 } 127 }, '-', { 128 text: '编辑', 129 tooltip: '编辑当前选择行数据', 130 iconCls: 'Pencil', 131 handler: function () { 132 RowEditing.cancelEdit(); 133 var data = Ext.getCmp("RoleGrid").getSelectionModel().getSelection(); 134 RowEditing.startEdit(data[0].index, 0); 135 }, 136 disabled: true 137 }, '-', { 138 itemId: 'removeUser', 139 text: '删除', 140 tooltip: '可以多选删除多条数据', 141 iconCls: 'Delete', 142 handler: function () { 143 Ext.MessageBox.confirm('提示', '确定删除该记录?', function (btn) { 144 if (btn != 'yes') { 145 return; 146 } 147 var sm = Ext.getCmp('RoleGrid').getSelectionModel(); 148 RowEditing.cancelEdit(); 149 150 var store = Ext.getCmp('RoleGrid').getStore(); 151 store.remove(sm.getSelection()); 152 store.sync(); //根据状态执行对应的服务器方法,delete,放在remove后才能成功执行服务器方法 153 if (store.getCount() > 0) { 154 sm.select(0); 155 } 156 }); 157 }, 158 disabled: true 159 }, '-', { 160 itemId: 'gridSync', 161 text: '保存', 162 tooltip: '保存到服务器', 163 iconCls: 'Disk', 164 handler: function () { 165 grid.store.sync(); 166 grid.store.commitChanges(); //执行commitChanges()提交数据修改。 167 } 168 }, '-', { 169 itemId: 'gridCancel', 170 text: '取消', 171 tooltip: '取消所有的已编辑数据', 172 iconCls: 'Decline', 173 handler: function () { 174 Ext.MessageBox.confirm('提示', '确定取消已编辑数据吗?', function (btn) { 175 if (btn != 'yes') { 176 return; 177 } 178 grid.store.rejectChanges(); //执行rejectChanges()撤销所有修改,将修改过的record恢复到原来的状态 179 }); 180 } 181 }, '-', { 182 itemId: 'gridrefresh', 183 text: '刷新', 184 tooltip: '重新加载数据', 185 iconCls: 'Arrowrefresh', 186 handler: function () { 187 grid.store.load(); 188 } 189 }, '->', { 190 itemId: 'ImportExcel', 191 text: '导入Excel', 192 tooltip: '导入角色数据', 193 iconCls: 'Pageexcel', 194 handler: function () { 195 Ext.MessageBox.show({ 196 title: '暂未开放', 197 msg: '即将开放', 198 icon: Ext.MessageBox.ERROR, 199 buttons: Ext.Msg.OK 200 }); 201 } 202 }, '-', { 203 itemId: 'ExportExcel', 204 text: '导出Ecxel', 205 tooltip: '角色数据导出Excel', 206 iconCls: 'Pageexcel', 207 handler: function () { 208 Ext.MessageBox.show({ 209 title: '暂未开放', 210 msg: '即将开放', 211 icon: Ext.MessageBox.ERROR, 212 buttons: Ext.Msg.OK 213 }); 214 } 215 } 216 ] 217 }); 218 var RowEditing = Ext.create('Ext.grid.plugin.RowEditing', { // 行编辑模式 219 clicksToEdit: 2, //双击进行修改 1-单击 2-双击 220 autoCancel: false, 221 saveBtnText: '确定', 222 cancelBtnText: '取消', 223 errorsText: '错误', 224 dirtyText: '你要确认或取消更改', 225 listeners: { 226 cancelEdit: function (rowEditing, context) { 227 // Canceling editing of a locally added, unsaved record: remove it 228 if (context.record.phantom) { //服务器上是否有此条记录的标志,true为没有 229 store.remove(context.record); 230 } 231 }, 232 Edit: function (rowEditing, context) { 233 //store.sync(); //根据状态执行对应的服务器方法,Add/Edit 234 var IsValidate = ValidateInput(context.record.data, context.record.phantom); 235 if (!IsValidate) { 236 grid.store.rejectChanges(); 237 } 238 }, 239 validateedit: function (rowEditing, context) { 240 241 } 242 } 243 }); 244 function ValidateInput(data, IsAdd) { 245 var IsValidate; 246 Ext.Ajax.request({ 247 url: ValidateInputUrl, 248 method: 'POST', 249 jsonData: data, 250 params: { IsAdd: IsAdd }, 251 async: false, 252 success: function (response) { 253 var resText = Ext.decode(response.responseText); 254 if (resText.success) { 255 Ext.MessageBox.alert('警告', resText.msg); 256 IsValidate = false; 257 } else { 258 IsValidate = true; 259 } 260 }, 261 failure: function (response, options) { 262 Ext.MessageBox.alert('服务器异常', response.status); 263 } 264 }); 265 return IsValidate; 266 } 267 //多选框变化 268 function selectchange() { 269 var count = this.getCount(); 270 //删除 271 if (count == 0) { 272 Gridtoolbar.items.items[2].disable(); 273 Gridtoolbar.items.items[4].disable(); 274 } 275 else { 276 Gridtoolbar.items.items[2].enable(); 277 Gridtoolbar.items.items[4].enable(); 278 } 279 } 280 //3.创建grid 281 var grid = Ext.create("Ext.grid.Panel", { 282 id: "RoleGrid", 283 xtype: "grid", 284 store: store, 285 columnLines: true, 286 renderTo: Ext.getBody(), 287 selModel: { 288 injectCheckbox: 0, 289 listeners: { 290 'selectionchange': selectchange 291 }, 292 mode: "MULTI", //"SINGLE"/"SIMPLE"/"MULTI" 293 checkOnly: false //只能通过checkbox选择 294 }, 295 selType: "checkboxmodel", 296 columns: [ 297 { xtype: "rownumberer", text: "序号", 40, align: 'center' }, 298 { id: "id", text: "ID", 40, dataIndex: 'ID', sortable: true, hidden: true }, 299 { text: '角色名称', dataIndex: 'Name', flex: 1, editor: "textfield" }, 300 { text: '角色描述', dataIndex: 'Description', flex: 1, editor: "textfield" }, 301 { text: '是否启用', dataIndex: 'IsUsed', flex: 1, xtype: 'checkcolumn', editor: { xtype: 'checkbox', cls: 'x-grid-checkheader-editor'} } 302 ], 303 plugins: [RowEditing], 304 listeners: { 305 itemdblclick: function (me, record, item, index, e, eOpts) { 306 //双击事件的操作 307 } 308 }, 309 tbar: Gridtoolbar, 310 bbar: { xtype: "pagingtoolbar", store: store, displayInfo: true, emptyMsg: "没有记录" } 311 }); 312 });
TreeGrid展示:前台代码和后台模型结合
Ext.onReady(function () { // ExtJS组件自适应浏览器大小改变,看还有没有其他实现方式 Ext.EventManager.onWindowResize(function () { Ext.ComponentManager.each(function (cmpId, cmp, length) { if (cmp.hasOwnProperty("renderTo")) { cmp.doLayout(); } }); }); Ext.create('Ext.container.Viewport', { layout: 'border', renderTo: Ext.getBody(), items: [{ title: '主菜单模块', region: 'west', xtype: 'panel', margins: '5 0 0 5', 200, collapsible: true, // 可折叠/展开 id: 'NavigationMenucontainer', layout: 'fit' }, { title: '子菜单列表', region: 'center', // 必须指定中间区域 xtype: 'panel', layout: 'fit', id: 'Gridcontainer', margins: '5 5 0 0' }] }); var NavigationMenu=Ext.getCmp('NavigationMenucontainer'); /** * 加载菜单树 */ Ext.Ajax.request({ url: AjaxPath, success: function (response) { var json = Ext.JSON.decode(response.responseText) Ext.each(json.data, function (el) { var panel = Ext.create( 'Ext.panel.Panel', { id: el.id, layout: 'fit' }); var ShowGrid=CreateGrid(el.id); Gridcontainer.add(ShowGrid); //初始化,加载主菜单下的菜单 panel.add(buildTree(el)); NavigationMenu.add(panel); }); }, failure: function (request) { Ext.MessageBox.show({ title: '操作提示', msg: "连接服务器失败", buttons: Ext.MessageBox.OK, icon: Ext.MessageBox.ERROR }); }, method: 'post' }); var Gridcontainer=Ext.getCmp('Gridcontainer'); /** * 组建树 */ Ext.define('TreeModelExtension', { extend: 'Ext.data.Model', //当Model实体类模型被用在某个TreeStore上,并且第一次实例化的时候 ,这些个属性会添加到Model实体类的的原型(prototype )上 (至于上述代码,则是通过把他设置为根节点的时候触发实例化处理的) fields: [ {name: 'text', type: 'string'}, {name: 'url', type: 'string'} ], }); var buildTree = function (json) { return Ext.create('Ext.tree.Panel', { id:'MenuTree', rootVisible: true, border: false, store: Ext.create('Ext.data.TreeStore', { model:'TreeModelExtension', root: { id:json.id, text:json.text, iconCls: json.iconCls, expanded: json.expanded, children: json.children } }), listeners: { 'itemclick': function (view, record, item, index, e) { var ParentID = record.get('id'); var ShowGrid=CreateGrid(ParentID); Gridcontainer.add(ShowGrid); }, scope: this } }); }; function CreateGrid(ParentID) { var Gridtoolbar = Ext.create('Ext.toolbar.Toolbar', { renderTo: document.body, items: [{ text: '新增', tooltip: '新增一条数据', iconCls: 'Add', handler: function () { RowEditing.cancelEdit(); // Create a model instance var r = new BeiDream.model.BeiDream_NavigationMenu(); Ext.getCmp('NavigationMenuGrid').getStore().insert(0, r); RowEditing.startEdit(0, 0); } }, '-', { text: '编辑', tooltip: '编辑当前选择行数据', iconCls: 'Pencil', handler: function () { RowEditing.cancelEdit(); var data = Ext.getCmp("NavigationMenuGrid").getSelectionModel().getSelection(); RowEditing.startEdit(data[0].index, 0); }, disabled: true }, '-', { itemId: 'removeUser', text: '删除', tooltip: '可以多选删除多条数据', iconCls: 'Delete', handler: function () { Ext.MessageBox.confirm('提示', '确定删除该记录?', function (btn) { if (btn != 'yes') { return; } var sm = Ext.getCmp('NavigationMenuGrid').getSelectionModel(); RowEditing.cancelEdit(); var store = Ext.getCmp('NavigationMenuGrid').getStore(); store.remove(sm.getSelection()); store.sync(); //根据状态执行对应的服务器方法,delete,放在remove后才能成功执行服务器方法 if (store.getCount() > 0) { sm.select(0); } }); }, disabled: true }, '-', { itemId: 'gridSync', text: '保存', tooltip: '保存到服务器', iconCls: 'Disk', handler: function () { var grid=Ext.getCmp('NavigationMenuGrid'); grid.store.sync(); grid.store.commitChanges(); //执行commitChanges()提交数据修改。 } }, '-', { itemId: 'gridCancel', text: '取消', tooltip: '取消所有的已编辑数据', iconCls: 'Decline', handler: function () { Ext.MessageBox.confirm('提示', '确定取消已编辑数据吗?', function (btn) { if (btn != 'yes') { return; } var grid=Ext.getCmp('NavigationMenuGrid'); grid.store.rejectChanges(); //执行rejectChanges()撤销所有修改,将修改过的record恢复到原来的状态 }); } }, '-', { itemId: 'gridrefresh', text: '刷新', tooltip: '重新加载数据', iconCls: 'Arrowrefresh', handler: function () { var grid=Ext.getCmp('NavigationMenuGrid'); grid.store.load(); } } ] }); var RowEditing = Ext.create('Ext.grid.plugin.RowEditing', { // 行编辑模式 clicksToEdit: 2, //双击进行修改 1-单击 2-双击 autoCancel: false, saveBtnText: '确定', cancelBtnText: '取消', errorsText: '错误', dirtyText: '你要确认或取消更改', listeners: { // beforeedit: function (rowEditing,e,context) { // if(e.colldx==2 && e.record.data.IsLeaf==false){ // return false; // }else{ // return true; // } // }, cancelEdit: function (rowEditing, context) { // Canceling editing of a locally added, unsaved record: remove it if (context.record.phantom) { //服务器上是否有此条记录的标志,true为没有 store.remove(context.record); } }, Edit: function (rowEditing, context) { //store.sync(); //根据状态执行对应的服务器方法,Add/Edit //var IsValidate = ValidateInput(context.record.data, context.record.phantom); // if (!IsValidate) { // grid.store.rejectChanges(); // } } } }); function ValidateInput(data, IsAdd) { var IsValidate; Ext.Ajax.request({ url: ValidateInputUrl, method: 'POST', jsonData: data, params: { IsAdd: IsAdd }, async: false, success: function (response) { var resText = Ext.decode(response.responseText); if (resText.success) { Ext.MessageBox.alert('警告', resText.msg); IsValidate = false; } else { IsValidate = true; } }, failure: function (response, options) { Ext.MessageBox.alert('服务器异常', response.status); } }); return IsValidate; } //多选框变化 function selectchange() { var count = this.getCount(); //删除 if (count == 0) { Gridtoolbar.items.items[2].disable(); Gridtoolbar.items.items[4].disable(); } else { Gridtoolbar.items.items[2].enable(); Gridtoolbar.items.items[4].enable(); } } //1.定义Model Ext.define("BeiDream.model.BeiDream_NavigationMenu", { extend: "Ext.data.Model", fields: [ { name: 'ID', type: 'int' }, { name: 'ParentID', type: 'int' }, { name: 'ShowName', type: 'string', defaultValue: '名称......' }, { name: 'IsLeaf', type: 'boolean', defaultValue: true }, { name: 'url', type: 'string' }, { name: 'OrderNo', type: 'int', defaultValue: 1 }, { name: 'iconCls', type: 'string' }, { name: 'Expanded', type: 'boolean', defaultValue: false } ] }); //2.创建store var store = Ext.create("Ext.data.Store", { model: "BeiDream.model.BeiDream_NavigationMenu", autoLoad: true, pageSize: 15, proxy: { type: 'ajax', api: { read: MenuListUrl, //查询 create: AddUrl, //创建 update: UpdateUrl, //更新,必须真正修改了才会触发 destroy: RemoveUrl //删除 }, reader: { type: 'json', root: 'data' }, writer: { type: 'json', //默认格式 //MVC下后台使用模型自动进行转换,如果是普通webform,则配置root:'data',encode:'true',这样之后就可以使用request【data】获取 writeAllFields: true, //false只提交修改过的字段 allowSingle: false //默认为true,为true时,一条数据不以数组形式提交,为false时,都以数组形式提交,这样避免了提交了一条数据时,后台是list模型无法接收到数据问题 }, listeners: { exception: function (proxy, response, operation) { // var grid=Ext.getCmp('NavigationMenuGrid'); // grid.store.load(); //删除失败,数据重新加载 var resText = Ext.decode(response.responseText); Ext.MessageBox.show({ title: '服务器端异常', msg: resText.msg, icon: Ext.MessageBox.ERROR, buttons: Ext.Msg.OK }); } } } }); store.on('beforeload', function (store, options) { var params = { ParentID: ParentID }; Ext.apply(store.proxy.extraParams, params); }); return Ext.create("Ext.grid.Panel", { id: "NavigationMenuGrid", xtype: "grid", store: store, columnLines: true, selModel: { injectCheckbox: 0, listeners: { 'selectionchange': selectchange }, mode: "SINGLE", //"SINGLE"/"SIMPLE"/"MULTI" checkOnly: false //只能通过checkbox选择 }, selType: "checkboxmodel", columns: [ { xtype: "rownumberer", text: "序号", 40, align: 'center' }, { id: "id", text: "ID", 40, dataIndex: 'ID', sortable: true, hidden: true }, { id: "id", text: "ParentID", 40, dataIndex: 'ParentID', sortable: true, hidden: true }, { text: '名称', dataIndex: 'ShowName', flex: 1, editor: { xtype: 'textfield', allowBlank: false } }, { text: '是否为模块', dataIndex: 'IsLeaf', flex: 1, xtype: 'checkcolumn', editor: { xtype: 'checkbox', cls: 'x-grid-checkheader-editor'} }, { text: '控制器路径', dataIndex: 'url', flex: 1, editor : { xtype: 'combobox', editable:false, listeners: { //点击下拉列表事件 expand: function (me, event, eOpts) { var grid=Ext.getCmp('NavigationMenuGrid'); var record = grid.getSelectionModel().getLastSelected(); if(record!=null){ if(record.data.IsLeaf==true){ currentComboBox = me; f_openSelectControllerWin(); }else{ Ext.MessageBox.alert('警告', '只有模块才拥有控制器!'); } } } } } }, { text: '排序号', dataIndex: 'OrderNo',align:"center", 48, flex: 1,editor: { xtype: 'numberfield', allowBlank: false, minValue: 1, maxValue: 150000 } }, { text: '图标', dataIndex: 'iconCls',align:"center", 48,renderer : function(value) { return "<div Align='center' style='height:16px;16px' class="+value+"></div>"; } ,editor : { xtype: 'combobox', editable:false, listeners: { //点击下拉列表事件 expand: function (me, event, eOpts) { currentComboBox = me; f_openIconsWin(); } } } }, { text: '是否展开', dataIndex: 'Expanded', flex: 1, xtype: 'checkcolumn', editor: { xtype: 'checkbox', cls: 'x-grid-checkheader-editor'} } ], plugins: [RowEditing], tbar: Gridtoolbar, bbar: { xtype: "pagingtoolbar", store: store, displayInfo: true, emptyMsg: "没有记录" } }); }; });
要点二:后台MVC的传参绑定,返回值自定义
MVC方便了Ajax的异步实现,并且方便的模型传参,代码如下
1 /// <summary> 2 /// 返回数据库新增后的实体,供前台的extjs的 grid的store更新数据,这样就不需要进行重新加载store了,删,改类似 3 /// </summary> 4 /// <param name="Roles"></param> 5 /// <returns></returns> 6 [Anonymous] 7 [HttpPost] 8 public ActionResult Add(List<BeiDream_Role> Roles) 9 { 10 List<BeiDream_Role> AddRoles = new List<BeiDream_Role>(); 11 List<Object> ListObj = RoleService.Add(Roles); 12 if (ListObj.Count == 0) 13 { 14 List<string> msg = new List<string>(); 15 msg.Add("添加角色失败!"); 16 return this.ExtjsJsonResult(false, msg); 17 } 18 else 19 { 20 foreach (var item in ListObj) 21 { 22 AddRoles.Add(RoleService.GetModelByID(item)); 23 } 24 List<string> msg = new List<string>(); 25 msg.Add("添加角色成功!"); 26 return this.ExtjsJsonResult(true, AddRoles, msg); 27 } 28 29 }
可以看到我直接通过后台模型来接收前台传递过来的参数,而不需要去一一解析参数值
返回值自定义:重写了ActionResult,实现了extjs需要的返回值
1 /// <summary> 2 /// 扩展的jsonResult模型,适用于extjs需要的json数据类型 3 /// </summary> 4 public class JsonResultExtension:ActionResult 5 { 6 public bool success { get; set; } 7 public string msg { get; set; } 8 public object data { get; set; } 9 public long? total { get; set; } 10 public Dictionary<string, string> errors { get; set; } 11 /// <summary> 12 /// 是否序列化为extjs需要的json格式,否则进行普通序列化 13 /// </summary> 14 public bool ExtjsUISerialize { get; set; } 15 public override void ExecuteResult(ControllerContext context) 16 { 17 18 if (context == null) 19 { 20 throw new ArgumentNullException("context"); 21 } 22 HttpResponseBase response = context.HttpContext.Response; 23 response.ContentType = "application/json"; 24 25 StringWriter sw = new StringWriter(); 26 //IsoDateTimeConverter timeFormat = new IsoDateTimeConverter(); 27 //timeFormat.DateTimeFormat = "yyyy-MM-dd HH:mm:ss"; 28 IsoDateTimeConverter timeFormat = new IsoDateTimeConverter(); 29 timeFormat.DateTimeFormat = "yyyy-MM-dd"; 30 JsonSerializer serializer = JsonSerializer.Create( 31 new JsonSerializerSettings 32 { 33 Converters = new JsonConverter[] { timeFormat }, 34 ReferenceLoopHandling = ReferenceLoopHandling.Ignore, 35 NullValueHandling = NullValueHandling.Ignore //忽略为null的值序列化 36 37 } 38 ); 39 40 41 using (JsonWriter jsonWriter = new JsonTextWriter(sw)) 42 { 43 jsonWriter.Formatting = Formatting.Indented; 44 45 if (ExtjsUISerialize) 46 serializer.Serialize(jsonWriter, this); 47 else 48 serializer.Serialize(jsonWriter, data); 49 } 50 response.Write(sw.ToString()); 51 52 } 53 }
特性标注及权限验证,代码如下
1 /// <summary> 2 /// 权限拦截 3 /// </summary> 4 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] 5 public class PermissionFilterAttribute : ActionFilterAttribute 6 { 7 /// <summary> 8 /// 权限拦截 9 /// </summary> 10 /// <param name="filterContext"></param> 11 public override void OnActionExecuting(ActionExecutingContext filterContext) 12 { 13 if (!this.CheckAnonymous(filterContext)) 14 { 15 //未登录验证 16 if (SessionHelper.Get("UserID") == null) 17 { 18 //跳转到登录页面 19 filterContext.RequestContext.HttpContext.Response.Redirect("~/Admin/User/Login"); 20 } 21 } 22 } 23 /// <summary> 24 /// [Anonymous标记]验证是否匿名访问 25 /// </summary> 26 /// <param name="filterContext"></param> 27 /// <returns></returns> 28 public bool CheckAnonymous(ActionExecutingContext filterContext) 29 { 30 //验证是否是匿名访问的Action 31 object[] attrsAnonymous = filterContext.ActionDescriptor.GetCustomAttributes(typeof(AnonymousAttribute), true); 32 //是否是Anonymous 33 var Anonymous = attrsAnonymous.Length == 1; 34 return Anonymous; 35 } 36 }
通过写一个BaseController来进行权限的验证,这样就不需要所有需要验证的Controller加标注了,当然BaseController还可以增加其他通用的处理
1 /// <summary> 2 /// Admin后台系统公共控制器(需要验证的模块) 3 /// </summary> 4 [PermissionFilter] 5 public class BaseController:Controller 6 { 7 8 }
要点三:轻量级ORM框架PetaPoco
非侵入性ORM框架,只需要一个PetaPoco.cs文件就OK了,不过我对其进行了再次封装,实现了工作单元,还是上代码吧
一:封装
二:使用,具体封装和使用,大家还是去下载源码看吧
要点四:依赖注入框架Autofac
目前使用心得最大的好处就是不需要配置即实现了面向接口编程,特别是和MVC结合,实现构造函数注入就更加方便了,当然它还有其他
功能,比如生命周期唯一实例,单例啊等等,暂时还研究不深,只是简单应用,大家看看具体实现吧
1 private static void AutofacMvcRegister() 2 { 3 ContainerBuilder builder = new ContainerBuilder(); 4 builder.RegisterGeneric(typeof(DbContextBase<>)).As(typeof(IDataRepository<>)); 5 Type baseType = typeof(IDependency); 6 Assembly[] assemblies = Assembly.GetExecutingAssembly().GetReferencedAssemblies() 7 .Select(Assembly.Load).ToArray(); 8 assemblies = assemblies.Union(new[] { Assembly.GetExecutingAssembly() }).ToArray(); 9 builder.RegisterAssemblyTypes(assemblies) 10 .Where(type => baseType.IsAssignableFrom(type) && !type.IsAbstract) 11 .AsImplementedInterfaces().InstancePerLifetimeScope();//InstancePerLifetimeScope 保证生命周期基于请求 12 13 //无效 14 //builder.RegisterType<DefaultCacheAdapter>().PropertiesAutowired().As<ICacheStorage>(); 15 16 builder.RegisterControllers(Assembly.GetExecutingAssembly()); 17 builder.RegisterFilterProvider(); 18 IContainer container = builder.Build(); 19 DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); 20 }
要点五;对象关系映射AutoMapper
目前也只是简单应用,先看代码,它是如何简化我们的工作量的
1 public static List<NavigationMenu> GetMapper(List<BeiDream_NavigationMenu> List) 2 { 3 List<NavigationMenu> NavigationMenuList = new List<NavigationMenu>(); 4 foreach (var item in List) 5 { 6 //NavigationMenu DaoModel = new NavigationMenu(); 7 //DaoModel.id = item.ID; 8 //DaoModel.text = item.ShowName; 9 //DaoModel.leaf = item.IsLeaf; 10 //DaoModel.url = item.url; 11 //DaoModel.Expanded = item.Expanded; 12 //DaoModel.children = null; 13 NavigationMenu DaoModel = item.ToDestination<BeiDream_NavigationMenu, NavigationMenu>(); 14 NavigationMenuList.Add(DaoModel); 15 } 16 return NavigationMenuList; 17 }
注释掉的是不使用automapper之前的代码,没注释掉的是使用automapper,扩展了方法直接一句代码实现转化,是不是很easy,当然实
现这些之前,我们需要给他定义规则,然后还要注册,代码如下,具体的请看源码
1 public class NavigationMenuProfile : Profile 2 { 3 protected override void Configure() 4 { 5 CreateMap<BeiDream_NavigationMenu, NavigationMenu>() 6 .ForMember(dest => dest.id, opt => opt.MapFrom(src => src.ID)) 7 .ForMember(dest => dest.text, opt => opt.MapFrom(src => src.ShowName)) 8 .ForMember(dest => dest.leaf, opt => opt.MapFrom(src => src.IsLeaf)); 9 } 10 }
要点六:数据验证
extjs前台验证我们已经做了,但是客户端传来的东西我们不能完全相信,后台需要再次验证,我们看到mvc的官方demo。一句话就实现
了验证,我们是不是可以自己做验证呢,看代码
1 [Anonymous] 2 public ActionResult SaveUser(BeiDream_User model, List<int> Roles) 3 { 4 var ValidateResult = Validation.Validate(model);//服务器端的验证 5 if (ValidateResult.IsValid) //验证成功 6 { 7 bool IsExitUser = UserService.PetaPocoDB.Exists<BeiDream_User>(model.ID); 8 if (!IsExitUser) 9 { 10 FilterGroup userRoleGroup = new FilterGroup(); 11 FilterHelper.CreateFilterGroup(userRoleGroup, null, "UserName", model.UserName, GroupOperatorQueryEnum.and, RuleOperatorQueryEnum.equal); 12 bool IsExist = UserService.IsExist(userRoleGroup); 13 if (IsExist) 14 { 15 List<string> errorName=new List<string>(); 16 errorName.Add("UserName"); 17 ValidationResult error = new ValidationResult("已存在相同的用户名", errorName); 18 ValidateResult.Add(error); 19 return this.ExtjsFromJsonResult(false,ValidateResult); 20 } 21 else 22 { 23 bool IsSaveSuccess = TransactionService.AddUserAndUserRole(model, Roles); 24 List<string> msg = new List<string>(); 25 msg.Add(IsSaveSuccess ? "用户信息保存成功!" : "用户信息保存失败!"); 26 return this.ExtjsFromJsonResult(true, null, msg); 27 } 28 } 29 else 30 { 31 FilterGroup userRoleGroup = new FilterGroup(); 32 FilterHelper.CreateFilterGroup(userRoleGroup, null, "UserName", model.UserName, GroupOperatorQueryEnum.and, RuleOperatorQueryEnum.equal); 33 FilterHelper.CreateFilterGroup(userRoleGroup, null, "ID", model.ID, GroupOperatorQueryEnum.and, RuleOperatorQueryEnum.notequal); 34 bool IsExist = UserService.IsExist(userRoleGroup); 35 if (IsExist) 36 { 37 List<string> errorName = new List<string>(); 38 errorName.Add("UserName"); 39 ValidationResult error = new ValidationResult("已存在相同的用户名", errorName); 40 ValidateResult.Add(error); 41 return this.ExtjsFromJsonResult(false, ValidateResult); 42 } 43 else 44 { 45 bool IsSaveSuccess = TransactionService.UpdateUserAndUserRole(model, Roles); 46 List<string> msg = new List<string>(); 47 msg.Add(IsSaveSuccess ? "用户信息保存成功!" : "用户信息保存失败!"); 48 return this.ExtjsFromJsonResult(true, null, msg); 49 } 50 } 51 } 52 else 53 { 54 return this.ExtjsFromJsonResult(false,ValidateResult); //验证失败,返回失败的验证结果,给出前台提示信息 55 } 56 }
大家可以看到前台传进来的参数,我们先进行验证 var ValidateResult = Validation.Validate(model),验证的条件我们是在模型上定义好的,然后判断验证是否通过,通过进行下一步动作,不通过,把验证的结果信息返回前台,提示给用户
要点七:SQL翻译机
这个只能算是一个简单的东西吧,并且感觉用起来麻烦,但是我觉得用的熟练了,还是很不错的,只是省了手拼SQL的问题嘛,减少了出
错几率,具体使用还是看代码吧
要点八:缓存
缓存也就简单应用Helper级别,主要用了.net自带缓存和分布式Memcached缓存,一个接口,两个实现
1 /// <summary> 2 /// 缓存接口 3 /// </summary> 4 public interface ICacheStorage 5 { 6 #region 缓存操作 7 /// <summary> 8 /// 添加缓存 9 /// </summary> 10 /// <param name="key"></param> 11 /// <param name="value"></param> 12 void Insert(string key, object value); 13 /// <summary> 14 /// 添加缓存(默认滑动时间为20分钟) 15 /// </summary> 16 /// <param name="key">key</param> 17 /// <param name="value">value</param> 18 /// <param name="expiration">绝对过期时间</param> 19 void Insert(string key, object value, DateTime expiration); 20 /// <summary> 21 /// 添加缓存 22 /// </summary> 23 /// <param name="key">key</param> 24 /// <param name="value">value</param> 25 /// <param name="expiration">过期时间</param> 26 void Insert(string key, object value, TimeSpan expiration); 27 /// <summary> 28 /// 获得key对应的value 29 /// </summary> 30 /// <param name="key"></param> 31 /// <returns></returns> 32 object Get(string key); 33 /// <summary> 34 /// 根据key删除缓存 35 /// </summary> 36 /// <param name="key"></param> 37 void Remove(string key); 38 /// <summary> 39 /// 缓存是否存在key的value 40 /// </summary> 41 /// <param name="key">key</param> 42 /// <returns></returns> 43 bool Exist(string key); 44 /// <summary> 45 /// 获取所有的缓存key 46 /// </summary> 47 /// <returns></returns> 48 List<string> GetCacheKeys(); 49 /// <summary> 50 /// 清空缓存 51 /// </summary> 52 void Flush(); 53 54 #endregion 55 }
写在最后
写博客真的是很累人的事,很敬佩那些能写连载博客的牛人们,虽然自己做的项目很小,但是觉得写成博客,要写的要点还是很多的
,上面我讲的很粗略,但是主要的知识点都讲出来了,这个项目其实没有做完,不打算再继续了,打算换了,接下来打算使用easyui
+knockout+ef来写一个完整的权限管理系统,涉及菜单权限、按钮权限、字段权限等等吧,路很长.....任重而道远
最后,大家如果觉得有帮助,请点推荐哦!源码下载地址: