前言
本项目使用的开发环境及技术列举如下:
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: "序号", width: 40, align: \'center\' },
298 { id: "id", text: "ID", width: 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\',
width: 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: "序号", width: 40, align: \'center\' },
{ id: "id", text: "ID", width: 40, dataIndex: \'ID\', sortable: true, hidden: true },
{ id: "id", text: "ParentID", width: 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", width: 48, flex: 1,editor: {
xtype: \'numberfield\',
allowBlank: false,
minValue: 1,
maxValue: 150000
} },
{ text: \'图标\', dataIndex: \'iconCls\',align:"center", width: 48,renderer : function(value) {
return "<div Align=\'center\' style=\'height:16px;width: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来写一个完整的权限管理系统,涉及菜单权限、按钮权限、字段权限等等吧,路很长.....任重而道远
最后,大家如果觉得有帮助,请点推荐哦!源码下载地址: