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的用法比较多,也是比较麻烦的地方,下面直接上代码,大家看看
首页布局:
View CodeGrid行内增删改查:
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了,不过我对其进行了再次封装,实现了工作单元,还是上代码吧
一:封装
View Code二:使用,具体封装和使用,大家还是去下载源码看吧
View Code要点四:依赖注入框架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的问题嘛,减少了出
错几率,具体使用还是看代码吧
View Code要点八:缓存
缓存也就简单应用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来写一个完整的权限管理系统,涉及菜单权限、按钮权限、字段权限等等吧,路很长.....任重而道远
最后,大家如果觉得有帮助,请点推荐哦!源码下载地址:
