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 Code
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了,不過我對其進行了再次封裝,實現了工作單元,還是上代碼吧
一:封裝
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來寫一個完整的權限管理系統,涉及菜單權限、按鈕權限、字段權限等等吧,路很長.....任重而道遠
最后,大家如果覺得有幫助,請點推薦哦!源碼下載地址:

