以前文章:《兩種讀寫配置文件的方案(app.config與web.config通用)》,如今從新整理一個更完善的版本,增長批量讀寫以及指定配置文件路徑,代碼以下:html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
|
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
System.Threading.Tasks;
using
System.Configuration;
using
System.IO;
namespace
Zuowj.Utils
{
/// <summary>
/// 配置工具類
/// Author:左文俊
/// Date:2018/1/27
public
static
class
ConfigUtil
{
/// <summary>
/// 獲取管理配置文件對象Configuration
/// </summary>
/// <param name="configPath">指定要管理的配置文件路徑,若是爲空或不存在,則管理程序集默認的配置文件路徑</param>
/// <returns></returns>
private
static
Configuration GetConfiguration(
string
configPath =
null
)
{
if
(!
string
.IsNullOrEmpty(configPath) && File.Exists(configPath))
{
ExeConfigurationFileMap map =
new
ExeConfigurationFileMap();
map.ExeConfigFilename = configPath;
return
ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
}
else
{
return
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
}
}
/// <summary>
/// 獲取指定配置文件+配置名稱的配置項的值
/// </summary>
public
static
string
GetAppSettingValue(
string
key,
string
defaultValue =
null
,
string
configPath =
null
)
{
var
config = GetConfiguration(configPath);
var
appSetting = config.AppSettings.Settings[key];
return
appSetting.Value;
}
/// <summary>
/// 設置配置值(存在則更新,不存在則新增)
/// </summary>
public
static
void
SetAppSettingValue(
string
key,
string
value,
string
configPath =
null
)
{
var
config = GetConfiguration(configPath);
var
setting = config.AppSettings.Settings[key];
if
(setting ==
null
)
{
config.AppSettings.Settings.Add(key, value);
}
else
{
setting.Value = value;
}
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection(
"appSettings"
);
}
/// <summary>
/// 刪除配置值
/// </summary>
public
static
void
RemoveAppSetting(
string
key,
string
configPath =
null
)
{
var
config = GetConfiguration(configPath);
config.AppSettings.Settings.Remove(key);
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection(
"appSettings"
);
}
/// <summary>
/// 設置多個配置值(存在則更新,不存在則新增)
/// </summary>
public
static
void
SetAppSettingValues(IEnumerable<KeyValuePair<
string
,
string
>> settingValues,
string
configPath =
null
)
{
var
config = GetConfiguration(configPath);
foreach
(
var
item
in
settingValues)
{
var
setting = config.AppSettings.Settings[item.Key];
if
(setting ==
null
)
{
config.AppSettings.Settings.Add(item.Key, item.Value);
}
else
{
setting.Value = item.Value;
}
}
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection(
"appSettings"
);
}
/// <summary>
/// 獲取全部配置值
/// </summary>
public
static
Dictionary<
string
,
string
> GetAppSettingValues(
string
configPath =
null
)
{
Dictionary<
string
,
string
> settingDic =
new
Dictionary<
string
,
string
>();
var
config = GetConfiguration(configPath);
var
settings = config.AppSettings.Settings;
foreach
(
string
key
in
settings.AllKeys)
{
settingDic[key] = settings[key].ToString();
}
return
settingDic;
}
/// <summary>
/// 刪除多個配置值
/// </summary>
public
static
void
RemoveAppSettings(
string
configPath =
null
,
params
string
[] keys)
{
var
config = GetConfiguration(configPath);
if
(keys !=
null
)
{
foreach
(
string
key
in
keys)
{
config.AppSettings.Settings.Remove(key);
}
}
else
{
config.AppSettings.Settings.Clear();
}
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection(
"appSettings"
);
}
/// <summary>
/// 獲取鏈接字符串
/// </summary>
public
static
string
GetConnectionString(
string
name,
string
defaultconnStr =
null
,
string
configPath =
null
)
{
var
config = GetConfiguration(configPath);
var
connStrSettings = config.ConnectionStrings.ConnectionStrings[name];
if
(connStrSettings ==
null
)
{
return
defaultconnStr;
}
return
connStrSettings.ConnectionString;
}
/// <summary>
/// 獲取指定配置文件+鏈接名稱的鏈接字符串配置項
/// </summary>
public
static
ConnectionStringSettings GetConnectionStringSetting(
string
name,
string
configPath =
null
)
{
var
config = GetConfiguration(configPath);
var
connStrSettings = config.ConnectionStrings.ConnectionStrings[name];
return
connStrSettings;
}
/// <summary>
/// 設置鏈接字符串的值(存在則更新,不存在則新增)
/// </summary>
public
static
void
SetConnectionString(
string
name,
string
connstr,
string
provider,
string
configPath =
null
)
{
var
config = GetConfiguration(configPath);
ConnectionStringSettings connStrSettings = config.ConnectionStrings.ConnectionStrings[name];
if
(connStrSettings !=
null
)
{
connStrSettings.ConnectionString = connstr;
connStrSettings.ProviderName = provider;
}
else
{
connStrSettings =
new
ConnectionStringSettings(name, connstr, provider);
config.ConnectionStrings.ConnectionStrings.Add(connStrSettings);
}
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection(
"connectionStrings"
);
}
/// <summary>
/// 刪除鏈接字符串配置項
/// </summary>
public
static
void
RemoveConnectionString(
string
name,
string
configPath =
null
)
{
var
config = GetConfiguration(configPath);
config.ConnectionStrings.ConnectionStrings.Remove(name);
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection(
"connectionStrings"
);
}
/// <summary>
/// 獲取全部的鏈接字符串配置項
/// </summary>
public
static
Dictionary<
string
, ConnectionStringSettings> GetConnectionStringSettings(
string
configPath =
null
)
{
var
config = GetConfiguration(configPath);
var
connStrSettingDic =
new
Dictionary<
string
, ConnectionStringSettings>();
var
connStrSettings = ConfigurationManager.ConnectionStrings;
foreach
(ConnectionStringSettings item
in
connStrSettings)
{
connStrSettingDic[item.Name] = item;
}
return
connStrSettingDic;
}
/// <summary>
/// 設置多個鏈接字符串的值(存在則更新,不存在則新增)
/// </summary>
public
static
void
SetConnectionStrings(IEnumerable<ConnectionStringSettings> connStrSettings,
string
configPath =
null
)
{
var
config = GetConfiguration(configPath);
foreach
(
var
item
in
connStrSettings)
{
ConnectionStringSettings connStrSetting = config.ConnectionStrings.ConnectionStrings[item.Name];
if
(connStrSetting !=
null
)
{
connStrSetting.ConnectionString = item.ConnectionString;
connStrSetting.ProviderName = item.ProviderName;
}
else
{
config.ConnectionStrings.ConnectionStrings.Add(item);
}
}
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection(
"connectionStrings"
);
}
/// <summary>
/// 刪除多個鏈接字符串配置項
/// </summary>
public
static
void
RemoveConnectionStrings(
string
configPath =
null
,
params
string
[] names)
{
var
config = GetConfiguration(configPath);
if
(names !=
null
)
{
foreach
(
string
name
in
names)
{
config.ConnectionStrings.ConnectionStrings.Remove(name);
}
}
else
{
config.ConnectionStrings.ConnectionStrings.Clear();
}
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection(
"connectionStrings"
);
}
}
}
|
基於MongoDb官方C#驅動封裝MongoDbCsharpHelper類(CRUD類)
近期工做中有使用到 MongoDb做爲日誌持久化對象,須要實現對MongoDb的增、刪、改、查,但因爲MongoDb的版本比較新,是2.4以上版本的,網上已有的一些MongoDb Helper類都是基於以前MongoDb舊的版本,沒法適用於新版本的MongoDb,故我基於MongoDb官方C#驅動從新封裝了MongoDbCsharpHelper類(CRUD類),完整代碼以下:前端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
|
using
MongoDB;
using
MongoDB.Bson;
using
MongoDB.Driver;
using
System;
using
System.Collections;
using
System.Collections.Generic;
using
System.Linq;
using
System.Linq.Expressions;
using
System.Reflection;
using
System.Threading;
using
System.Web;
namespace
Zuowj.Utils
{
/// <summary>
/// MongoDbCsharpHelper:MongoDb基於C#語言操做幫助類
/// Author:Zuowenjun
/// Date:2017/11/16
/// </summary>
public
class
MongoDbCsharpHelper
{
private
readonly
string
connectionString =
null
;
private
readonly
string
databaseName =
null
;
private
MongoDB.Driver.IMongoDatabase database =
null
;
private
readonly
bool
autoCreateDb =
false
;
private
readonly
bool
autoCreateCollection =
false
;
static
MongoDbCsharpHelper()
{
BsonDefaults.GuidRepresentation = GuidRepresentation.Standard;
}
public
MongoDbCsharpHelper(
string
mongoConnStr,
string
dbName,
bool
autoCreateDb =
false
,
bool
autoCreateCollection =
false
)
{
this
.connectionString = mongoConnStr;
this
.databaseName = dbName;
this
.autoCreateDb = autoCreateDb;
this
.autoCreateCollection = autoCreateCollection;
}
#region 私有方法
private
MongoClient CreateMongoClient()
{
return
new
MongoClient(connectionString);
}
private
MongoDB.Driver.IMongoDatabase GetMongoDatabase()
{
if
(database ==
null
)
{
var
client = CreateMongoClient();
if
(!DatabaseExists(client, databaseName) && !autoCreateDb)
{
throw
new
KeyNotFoundException(
"此MongoDB名稱不存在:"
+ databaseName);
}
database = CreateMongoClient().GetDatabase(databaseName);
}
return
database;
}
private
bool
DatabaseExists(MongoClient client,
string
dbName)
{
try
{
var
dbNames = client.ListDatabases().ToList().Select(db => db.GetValue(
"name"
).AsString);
return
dbNames.Contains(dbName);
}
catch
//若是鏈接的帳號不能枚舉出全部DB會報錯,則默認爲true
{
return
true
;
}
}
private
bool
CollectionExists(IMongoDatabase database,
string
collectionName)
{
var
options =
new
ListCollectionsOptions
{
Filter = Builders<BsonDocument>.Filter.Eq(
"name"
, collectionName)
};
return
database.ListCollections(options).ToEnumerable().Any();
}
private
MongoDB.Driver.IMongoCollection<TDoc> GetMongoCollection<TDoc>(
string
name, MongoCollectionSettings settings =
null
)
{
var
mongoDatabase = GetMongoDatabase();
if
(!CollectionExists(mongoDatabase, name) && !autoCreateCollection)
{
throw
new
KeyNotFoundException(
"此Collection名稱不存在:"
+ name);
}
return
mongoDatabase.GetCollection<TDoc>(name, settings);
}
private
List<UpdateDefinition<TDoc>> BuildUpdateDefinition<TDoc>(
object
doc,
string
parent)
{
var
updateList =
new
List<UpdateDefinition<TDoc>>();
foreach
(
var
property
in
typeof
(TDoc).GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
var
key = parent ==
null
? property.Name :
string
.Format(
"{0}.{1}"
, parent, property.Name);
//非空的複雜類型
if
((property.PropertyType.IsClass || property.PropertyType.IsInterface) && property.PropertyType !=
typeof
(
string
) && property.GetValue(doc) !=
null
)
{
if
(
typeof
(IList).IsAssignableFrom(property.PropertyType))
{
#region 集合類型
int
i = 0;
var
subObj = property.GetValue(doc);
foreach
(
var
item
in
subObj
as
IList)
{
if
(item.GetType().IsClass || item.GetType().IsInterface)
{
updateList.AddRange(BuildUpdateDefinition<TDoc>(doc,
string
.Format(
"{0}.{1}"
, key, i)));
}
else
{
updateList.Add(Builders<TDoc>.Update.Set(
string
.Format(
"{0}.{1}"
, key, i), item));
}
i++;
}
#endregion
}
else
{
#region 實體類型
//複雜類型,導航屬性,類對象和集合對象
var
subObj = property.GetValue(doc);
foreach
(
var
sub
in
property.PropertyType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
updateList.Add(Builders<TDoc>.Update.Set(
string
.Format(
"{0}.{1}"
, key, sub.Name), sub.GetValue(subObj)));
}
#endregion
}
}
else
//簡單類型
{
updateList.Add(Builders<TDoc>.Update.Set(key, property.GetValue(doc)));
}
}
return
updateList;
}
private
void
CreateIndex<TDoc>(IMongoCollection<TDoc> col,
string
[] indexFields, CreateIndexOptions options =
null
)
{
if
(indexFields ==
null
)
{
return
;
}
var
indexKeys = Builders<TDoc>.IndexKeys;
IndexKeysDefinition<TDoc> keys =
null
;
if
(indexFields.Length > 0)
{
keys = indexKeys.Descending(indexFields[0]);
}
for
(
var
i = 1; i < indexFields.Length; i++)
{
var
strIndex = indexFields[i];
keys = keys.Descending(strIndex);
}
if
(keys !=
null
)
{
col.Indexes.CreateOne(keys, options);
}
}
#endregion
public
void
CreateCollectionIndex<TDoc>(
string
collectionName,
string
[] indexFields, CreateIndexOptions options =
null
)
{
CreateIndex(GetMongoCollection<TDoc>(collectionName), indexFields, options);
}
public
void
CreateCollection<TDoc>(
string
[] indexFields =
null
, CreateIndexOptions options =
null
)
{
string
collectionName =
typeof
(TDoc).Name;
CreateCollection<TDoc>(collectionName, indexFields, options);
}
public
void
CreateCollection<TDoc>(
string
collectionName,
string
[] indexFields =
null
, CreateIndexOptions options =
null
)
{
var
mongoDatabase = GetMongoDatabase();
mongoDatabase.CreateCollection(collectionName);
CreateIndex(GetMongoCollection<TDoc>(collectionName), indexFields, options);
}
public
List<TDoc> Find<TDoc>(Expression<Func<TDoc,
bool
>> filter, FindOptions options =
null
)
{
string
collectionName =
typeof
(TDoc).Name;
return
Find<TDoc>(collectionName, filter, options);
}
public
List<TDoc> Find<TDoc>(
string
collectionName, Expression<Func<TDoc,
bool
>> filter, FindOptions options =
null
)
{
var
colleciton = GetMongoCollection<TDoc>(collectionName);
return
colleciton.Find(filter, options).ToList();
}
public
List<TDoc> FindByPage<TDoc, TResult>(Expression<Func<TDoc,
bool
>> filter, Expression<Func<TDoc, TResult>> keySelector,
int
pageIndex,
int
pageSize,
out
int
rsCount)
{
string
collectionName =
typeof
(TDoc).Name;
return
FindByPage<TDoc, TResult>(collectionName, filter, keySelector, pageIndex, pageSize,
out
rsCount);
}
public
List<TDoc> FindByPage<TDoc, TResult>(
string
collectionName, Expression<Func<TDoc,
bool
>> filter, Expression<Func<TDoc, TResult>> keySelector,
int
pageIndex,
int
pageSize,
out
int
rsCount)
{
var
colleciton = GetMongoCollection<TDoc>(collectionName);
rsCount = colleciton.AsQueryable().Where(filter).Count();
int
pageCount = rsCount / pageSize + ((rsCount % pageSize) > 0 ? 1 : 0);
if
(pageIndex > pageCount) pageIndex = pageCount;
if
(pageIndex <= 0) pageIndex = 1;
return
colleciton.AsQueryable(
new
AggregateOptions { AllowDiskUse =
true
}).Where(filter).OrderByDescending(keySelector).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
}
public
void
Insert<TDoc>(TDoc doc, InsertOneOptions options =
null
)
{
string
collectionName =
typeof
(TDoc).Name;
Insert<TDoc>(collectionName, doc, options);
}
public
void
Insert<TDoc>(
string
collectionName, TDoc doc, InsertOneOptions options =
null
)
{
var
colleciton = GetMongoCollection<TDoc>(collectionName);
colleciton.InsertOne(doc, options);
}
public
void
InsertMany<TDoc>(IEnumerable<TDoc> docs, InsertManyOptions options =
null
)
{
string
collectionName =
typeof
(TDoc).Name;
InsertMany<TDoc>(collectionName, docs, options);
}
public
void
InsertMany<TDoc>(
string
collectionName, IEnumerable<TDoc> docs, InsertManyOptions options =
null
)
{
var
colleciton = GetMongoCollection<TDoc>(collectionName);
colleciton.InsertMany(docs, options);
}
public
void
Update<TDoc>(TDoc doc, Expression<Func<TDoc,
bool
>> filter, UpdateOptions options =
null
)
{
string
collectionName =
typeof
(TDoc).Name;
var
colleciton = GetMongoCollection<TDoc>(collectionName);
List<UpdateDefinition<TDoc>> updateList = BuildUpdateDefinition<TDoc>(doc,
null
);
colleciton.UpdateOne(filter, Builders<TDoc>.Update.Combine(updateList), options);
}
public
void
Update<TDoc>(
string
collectionName, TDoc doc, Expression<Func<TDoc,
bool
>> filter, UpdateOptions options =
null
)
{
var
colleciton = GetMongoCollection<TDoc>(collectionName);
List<UpdateDefinition<TDoc>> updateList = BuildUpdateDefinition<TDoc>(doc,
null
);
colleciton.UpdateOne(filter, Builders<TDoc>.Update.Combine(updateList), options);
}
public
void
Update<TDoc>(TDoc doc, Expression<Func<TDoc,
bool
>> filter, UpdateDefinition<TDoc> updateFields, UpdateOptions options =
null
)
{
string
collectionName =
typeof
(TDoc).Name;
Update<TDoc>(collectionName, doc, filter, updateFields, options);
}
public
void
Update<TDoc>(
string
collectionName, TDoc doc, Expression<Func<TDoc,
bool
>> filter, UpdateDefinition<TDoc> updateFields, UpdateOptions options =
null
)
{
var
colleciton = GetMongoCollection<TDoc>(collectionName);
colleciton.UpdateOne(filter, updateFields, options);
}
public
void
UpdateMany<TDoc>(TDoc doc, Expression<Func<TDoc,
bool
>> filter, UpdateOptions options =
null
)
{
string
collectionName =
typeof
(TDoc).Name;
UpdateMany<TDoc>(collectionName, doc, filter, options);
}
public
void
UpdateMany<TDoc>(
string
collectionName, TDoc doc, Expression<Func<TDoc,
bool
>> filter, UpdateOptions options =
null
)
{
var
colleciton = GetMongoCollection<TDoc>(collectionName);
List<UpdateDefinition<TDoc>> updateList = BuildUpdateDefinition<TDoc>(doc,
null
);
colleciton.UpdateMany(filter, Builders<TDoc>.Update.Combine(updateList), options);
}
public
void
Delete<TDoc>(Expression<Func<TDoc,
bool
>> filter, DeleteOptions options =
null
)
{
string
collectionName =
typeof
(TDoc).Name;
Delete<TDoc>(collectionName, filter, options);
}
public
void
Delete<TDoc>(
string
collectionName, Expression<Func<TDoc,
bool
>> filter, DeleteOptions options =
null
)
{
var
colleciton = GetMongoCollection<TDoc>(collectionName);
colleciton.DeleteOne(filter, options);
}
public
void
DeleteMany<TDoc>(Expression<Func<TDoc,
bool
>> filter, DeleteOptions options =
null
)
{
string
collectionName =
typeof
(TDoc).Name;
DeleteMany<TDoc>(collectionName, filter, options);
}
public
void
DeleteMany<TDoc>(
string
collectionName, Expression<Func<TDoc,
bool
>> filter, DeleteOptions options =
null
)
{
var
colleciton = GetMongoCollection<TDoc>(collectionName);
colleciton.DeleteMany(filter, options);
}
public
void
ClearCollection<TDoc>(
string
collectionName)
{
var
colleciton = GetMongoCollection<TDoc>(collectionName);
var
inddexs = colleciton.Indexes.List();
List<IEnumerable<BsonDocument>> docIndexs =
new
List<IEnumerable<BsonDocument>>();
while
(inddexs.MoveNext())
{
docIndexs.Add(inddexs.Current);
}
var
mongoDatabase = GetMongoDatabase();
mongoDatabase.DropCollection(collectionName);
if
(!CollectionExists(mongoDatabase, collectionName))
{
CreateCollection<TDoc>(collectionName);
}
if
(docIndexs.Count > 0)
{
colleciton = mongoDatabase.GetCollection<TDoc>(collectionName);
foreach
(
var
index
in
docIndexs)
{
foreach
(IndexKeysDefinition<TDoc> indexItem
in
index)
{
try
{
colleciton.Indexes.CreateOne(indexItem);
}
catch
{ }
}
}
}
}
}
}
|
對上述代碼中幾個特別的點進行簡要說明:web
1.因爲MongoClient.GetDatabase 獲取DB、MongoClient.GetCollection<TDoc> 獲取文檔(也可稱爲表)的方法 都有一個特色,即:若是指定的DB名稱、Collection名稱不存在,則會直接建立,但有的時候多是由於DB名稱、Collection名稱寫錯了致使誤建立了的DB或Collection,那就引發沒必要要的麻煩,故在MongoDbCsharpHelper類類內部封裝了兩個私有的方法:DatabaseExists(判斷DB是否存在,如是鏈接的帳號沒有檢索DB的權限可能會報錯,故代碼中加了直接返回true)、CollectionExists(判斷Collection是否存在);算法
2.每一個CRUD方法,我都分別重載了兩個方法,一個是無需指定Collection名稱,一個是須要指定Collection名稱,爲何這麼作呢?緣由很簡單,由於有時Collection的結構是相同的但又是不一樣的Collection,這時TDoc是同一個實體類,但collectionName倒是不一樣的;sql
3.分頁查詢的時候若是Collection的數據量比較大,那麼就會報相似錯誤:exception: Sort exceeded memory limit of 104857600 bytes, but did not opt in to external sorting. Aborting operation. Pass allowDiskUse:true,根據報錯提示,咱們在查詢大數據量時增長AggregateOptions對象,如: colleciton.AsQueryable(new AggregateOptions { AllowDiskUse = true })shell
4.ClearCollection清除Collection的全部數據,若是Collection的數據量很是大,那麼直接使用colleciton.DeleteMany可能須要好久,有沒有相似SQL SERVER 的truncate table的方法呢?通過多方論證,很遺憾並無找到同類功能的方法,只有DropCollection方法,而這個DropCollection方法是直接刪除Collection,固然包括Collection的全部數據,效率也很是高,可是因爲是Drop,Collection就不存在了,若是再訪問有可能會報Collection不存在的錯誤,那有沒有好的辦法解決了,固然有,那就是先DropCollection 而後再CreateCollection,最後別忘了把原有的索引插入到新建立的Collection中,這樣就實現了truncate 初始化表的做用,固然在建立索引的時候,有的時候可能報報錯(如:_id),由於_id默認就會被建立索引,再建立可能就會報錯,故colleciton.Indexes.CreateOne外我加了try catch,若是報錯則忽略。數據庫
5.CreateCollection(建立集合)、CreateCollectionIndex(建立集合索引)由於有的時候咱們須要明確的去建立一個Collection或對已有的Collection建立索引,若是經過shell命令會很是不方便,故在此封裝了一下。編程
使用示例以下:json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
var
mongoDbHelper =
new
MongoDbCsharpHelper(
"MongoDbConnectionString"
,
"LogDB"
);
mongoDbHelper.CreateCollection<SysLogInfo>(
"SysLog1"
,
new
[]{
"LogDT"
});
mongoDbHelper.Find<SysLogInfo>(
"SysLog1"
, t => t.Level ==
"Info"
);
int
rsCount=0;
mongoDbHelper.FindByPage<SysLogInfo, SysLogInfo>(
"SysLog1"
,t=>t.Level==
"Info"
,t=>t,1,20,
out
rsCount);
mongoDbHelper.Insert<SysLogInfo>(
"SysLog1"
,
new
SysLogInfo { LogDT = DateTime.Now, Level =
"Info"
, Msg =
"測試消息"
});
mongoDbHelper.Update<SysLogInfo>(
"SysLog1"
,
new
SysLogInfo { LogDT = DateTime.Now, Level =
"Error"
, Msg =
"測試消息2"
},t => t.LogDT==
new
DateTime(1900,1,1));
mongoDbHelper.Delete<SysLogInfo>(t => t.Level ==
"Info"
);
mongoDbHelper.ClearCollection<SysLogInfo>(
"SysLog1"
);
|
基於ASP.NET WEB API實現分佈式數據訪問中間層(提供對數據庫的CRUD)
一些小的C/S項目(winform、WPF等),因須要訪問操做數據庫,但又不能把DB鏈接配置在客戶端上,緣由有不少,多是DB鏈接沒法直接訪問,或客戶端不想安裝各類DB訪問組件,或DB鏈接不想暴露在客戶端(即便加密鏈接字符串仍有可能被破解的狀況),總之都是出於安全考慮,同時因項目小,也無需採用分佈式架構來將業務操做封裝到服務端,但又想保證客戶端業務的正常處理,這時咱們就能夠利用ASP.NET WEB API框架開發一個簡單的提供對數據庫的直接操做(CRUD)框架,簡稱爲:分佈式數據訪問中間層。api
實現方案很簡單,就是利用ASP.NET WEB API框架編寫於一個DataController,而後在DataController分別實現CRUD相關的公開ACTION方法便可,具體實現代碼以下:(由於邏輯簡單,一看就懂,故下面再也不詳細說明邏輯,文末會有一些總結)
ASP.NET WEB API服務端相關核心代碼:
1.DataController代碼:
[SqlInjectionFilter] [Authorize] public class DataController : ApiController { [AllowAnonymous] [HttpPost] public ApiResultInfo Login([FromBody]string[] loginInfo) { ApiResultInfo loginResult = null; try { if (loginInfo == null || loginInfo.Length != 4) { throw new Exception("登陸信息不全。"); } using (var da = BaseUtil.CreateDataAccess()) { if (用戶名及密碼判斷邏輯) { throw new Exception("登陸名或密碼錯誤。"); } else { string token = Guid.NewGuid().ToString("N"); HttpRuntime.Cache.Insert(Constants.CacheKey_SessionTokenPrefix + token, loginInfo[0], null, Cache.NoAbsoluteExpiration, TimeSpan.FromHours(1)); //登陸成功後須要處理的邏輯 loginResult = ApiResultInfo.BuildOKResult(token); } } } catch (Exception ex) { LogUitl.Error(ex, "Api.Data.Login", BaseUtil.SerializeToJson(loginInfo)); loginResult = ApiResultInfo.BuildErrResult("LoginErr", ex.Message); } return loginResult; } [HttpPost] public ApiResultInfo LogOut([FromBody] string token) { try { if (!string.IsNullOrEmpty(token)) { if (HttpRuntime.Cache[Constants.CacheKey_SessionTokenPrefix + token] != null) { HttpRuntime.Cache.Remove(token); } using (var da = BaseUtil.CreateDataAccess()) { //登出後須要處理的邏輯 } } } catch { } return ApiResultInfo.BuildOKResult(); } [HttpPost] public ApiResultInfo GetValue([FromBody]SqlCmdInfo sqlCmd) { using (var da = BaseUtil.CreateDataAccess()) { var result = da.ExecuteScalar<string>(sqlCmd.SqlCmdText, sqlCmd.GetCommandType(), sqlCmd.Parameters.TryToArray()); return ApiResultInfo.BuildOKResult(result); } } [Compression] [HttpPost] public ApiResultInfo GetDataSet([FromBody]SqlCmdInfo sqlCmd) { using (var da = BaseUtil.CreateDataAccess()) { var ds = da.ExecuteDataSet(sqlCmd.SqlCmdText, sqlCmd.GetCommandType(), sqlCmd.Parameters.TryToArray()); return ApiResultInfo.BuildOKResult(ds); } } [Compression] [HttpPost] public ApiResultInfo GetDataTable([FromBody]SqlCmdInfo sqlCmd) { using (var da = BaseUtil.CreateDataAccess()) { var table = da.ExecuteDataTable(sqlCmd.SqlCmdText, sqlCmd.GetCommandType(), sqlCmd.Parameters.TryToArray()); return ApiResultInfo.BuildOKResult(table); } } [HttpPost] public ApiResultInfo ExecuteCommand([FromBody]SqlCmdInfo sqlCmd) { using (var da = BaseUtil.CreateDataAccess()) { int result = da.ExecuteCommand(sqlCmd.SqlCmdText, sqlCmd.GetCommandType(), sqlCmd.Parameters.TryToArray()); return ApiResultInfo.BuildOKResult(result); } } [HttpPost] public ApiResultInfo BatchExecuteCommand([FromBody] IEnumerable<SqlCmdInfo> sqlCmds) { using (var da = BaseUtil.CreateDataAccess()) { int execCount = 0; da.UseTransaction(); foreach (var sqlCmd in sqlCmds) { execCount += da.ExecuteCommand(sqlCmd.SqlCmdText, sqlCmd.GetCommandType(), sqlCmd.Parameters.TryToArray()); } da.Commit(); return new ApiResultInfo(execCount > 0); } } [HttpPost] public async Task<ApiResultInfo> ExecuteCommandAsync([FromBody]SqlCmdInfo sqlCmd) { return await Task.Factory.StartNew((arg) => { var sqlCmdObj = arg as SqlCmdInfo; string connName = BaseUtil.GetDbConnectionName(sqlCmdObj.DbType); using (var da = BaseUtil.CreateDataAccess(connName)) { try { int result = da.ExecuteCommand(sqlCmdObj.SqlCmdText, sqlCmdObj.GetCommandType(), sqlCmdObj.Parameters.TryToArray()); return ApiResultInfo.BuildOKResult(result); } catch (Exception ex) { LogUitl.Error(ex, "Api.Data.ExecuteCommandAsync", BaseUtil.SerializeToJson(sqlCmdObj)); return ApiResultInfo.BuildErrResult("ExecuteCommandAsyncErr", ex.Message, new Dictionary<string, object> { { "StackTrace", ex.StackTrace } }); } } }, sqlCmd); } [HttpPost] public IHttpActionResult SaveLog([FromBody]string[] logInfo) { if (logInfo == null || logInfo.Length < 3) { return Ok(); } string[] saveLogInfo = new string[7]; for (int i = 1; i < logInfo.Length; i++) { if (saveLogInfo.Length > i + 1) { saveLogInfo[i] = logInfo[i]; } } switch (saveLogInfo[0].ToUpperInvariant()) { case "ERR": { LogUitl.Error(saveLogInfo[1], saveLogInfo[2], saveLogInfo[3], saveLogInfo[4], saveLogInfo[5], saveLogInfo[6]); break; } case "WARN": { LogUitl.Warn(saveLogInfo[1], saveLogInfo[2], saveLogInfo[3], saveLogInfo[4], saveLogInfo[5], saveLogInfo[6]); break; } case "INFO": { LogUitl.Info(saveLogInfo[1], saveLogInfo[2], saveLogInfo[3], saveLogInfo[4], saveLogInfo[5], saveLogInfo[6]); break; } } return Ok(); } }
2.SqlInjectionFilterAttribute (防止SQL注入、危險關鍵字攻擊過濾器)
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public class SqlInjectionFilterAttribute : ActionFilterAttribute { public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) { if (actionContext.ActionArguments.ContainsKey("sqlCmd")) { var sqlCmd = actionContext.ActionArguments["sqlCmd"] as SqlCmdInfo; if (BaseUtil.IsIncludeDangerSql(sqlCmd.SqlCmdText)) { throw new Exception("存在SQL注入風險,禁止操做!"); } } base.OnActionExecuting(actionContext); } }
IsIncludeDangerSql:判斷是否包含危險關鍵字
/// <summary> /// 判斷是否包含危險的SQL關鍵詞 /// </summary> /// <param name="sqlCmdText"></param> /// <returns>包含返回true,不然false</returns> public static bool IsIncludeDangerSql(string sqlCmdText) { if (string.IsNullOrWhiteSpace(sqlCmdText)) return false; sqlCmdText = sqlCmdText.Replace("[", " ").Replace("]", " "); //string dangerSqlObjs = @"sys\.columns|sys\.tables|sys\.views|sys\.objects|sys\.procedures|sys\.indexes|INFORMATION_SCHEMA\.TABLES|INFORMATION_SCHEMA\.VIEWS|INFORMATION_SCHEMA\.COLUMNS|GRANT|DENY|SP_HELP|SP_HELPTEXT"; //dangerSqlObjs += @"|object_id|syscolumns|sysobjects|sysindexes|drop\s+\w+|alter\s+\w+|create\s+\w+"; string dangerSqlObjs = @"sys\.\w+|INFORMATION_SCHEMA\.\w+|GRANT|DENY|SP_HELP|SP_HELPTEXT|sp_executesql"; dangerSqlObjs += @"|object_id|syscolumns|sysobjects|sysindexes|exec\s+\(.+\)|(create|drop|alter)\s+(database|table|index|procedure|view|trigger)\s+\w+(?!#)"; string patternStr = string.Format(@"(^|\s|,|\.)({0})(\s|,|\(|;|$)", dangerSqlObjs); bool mathed = Regex.IsMatch(sqlCmdText, patternStr, RegexOptions.IgnoreCase); if (mathed) { //TODO:記錄到危險請求表中,以便後續追查 LogUitl.Warn("檢測到包含危險的SQL關鍵詞語句:" + sqlCmdText, "IsIncludeDangerSql"); } return mathed; }
3.SqlCmdInfo (ACTION參數對象,SQL命令信息類)
[Serializable] public class SqlCmdInfo { public string SqlCmdText { get; set; } public ArrayList Parameters { get; set; } public bool IsSPCmdType { get; set; } public int DbType { get; set; } public CommandType GetCommandType() { return IsSPCmdType ? CommandType.StoredProcedure : CommandType.Text; } }
4.CompressionAttribute(壓縮返回內容過濾器,當返回的是大量數據時,能夠標記該過濾器,以便提升響應速度)
/// <summary> /// 壓縮返回信息 /// </summary> [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public class CompressionAttribute : ActionFilterAttribute { public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { var content = actionExecutedContext.Response.Content; #region 根據請求是否壓縮,暫時不用 ////var acceptEncoding = actionExecutedContext.Request.Headers.AcceptEncoding. //// Where(x => x.Value == "gzip" || x.Value == "deflate").ToList(); ////if (acceptEncoding.HasItem() && content != null && actionExecutedContext.Request.Method != HttpMethod.Options) ////{ //// var first = acceptEncoding.FirstOrDefault(); //// if (first != null) //// { //// var bytes = content.ReadAsByteArrayAsync().Result; //// switch (first.Value) //// { //// case "gzip": //// actionExecutedContext.Response.Content = new ByteArrayContent(CompressionHelper.GZipBytes(bytes)); //// actionExecutedContext.Response.Content.Headers.Add("Content-Encoding", "gzip"); //// break; //// case "deflate": //// actionExecutedContext.Response.Content = new ByteArrayContent(CompressionHelper.DeflateBytes(bytes)); //// actionExecutedContext.Response.Content.Headers.Add("Content-encoding", "deflate"); //// break; //// } //// } ////} #endregion //只要使用了CompressionAttribute,則默認使用GZIP壓縮 var bytes = content.ReadAsByteArrayAsync().Result; actionExecutedContext.Response.Content = new ByteArrayContent(CompressionHelper.GZipBytes(bytes)); actionExecutedContext.Response.Content.Headers.Add("Content-Encoding", "gzip"); base.OnActionExecuted(actionExecutedContext); } } /// <summary> /// 壓縮幫助類 /// </summary> internal static class CompressionHelper { public static byte[] DeflateBytes(byte[] bytes) { if (bytes == null || bytes.Length == 0) { return null; } using (var output = new MemoryStream()) { using (var compressor = new DeflateStream(output, CompressionMode.Compress, false)) { compressor.Write(bytes, 0, bytes.Length); } return output.ToArray(); } } public static byte[] GZipBytes(byte[] bytes) { if (bytes == null || bytes.Length == 0) { return null; } using (var output = new MemoryStream()) { using (var compressor = new GZipStream(output, CompressionMode.Compress, false)) { compressor.Write(bytes, 0, bytes.Length); } return output.ToArray(); } } }
5.RequestAuthenticationHandler (驗證請求合法性處理管道(包含請求內容解密),即:未正確登陸則不能調API操做數據庫)
public class RequestAuthenticationHandler : DelegatingHandler { private const string rsaPrivateKey = "私鑰字符串"; protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { try { //驗證TOKEN HttpRequestHeaders headers = request.Headers; IEnumerable<string> tokenHeaders = null; if (headers.TryGetValues("AccessToken", out tokenHeaders) && tokenHeaders.Any()) { string loginID = TokenVerification(tokenHeaders.ElementAt(0)); if (!string.IsNullOrEmpty(loginID)) { var principal = new GenericPrincipal(new GenericIdentity(loginID, "token"), null); Thread.CurrentPrincipal = principal; if (HttpContext.Current != null) { HttpContext.Current.User = principal; } } } IEnumerable<string> encryptHeaders=null; if (headers.TryGetValues("Encryption", out encryptHeaders) && encryptHeaders.Any()) { if (encryptHeaders.ElementAt(0) == "1") { //私鑰解密請求體內容 var originContent = request.Content; string requestData = await request.Content.ReadAsStringAsync(); string deContentStr = EncryptUtility.RSADecrypt(rsaPrivateKey, requestData); request.Content = new StringContent(deContentStr); request.Content.Headers.Clear(); foreach (var header in originContent.Headers) { request.Content.Headers.Add(header.Key, header.Value); } } } } catch (Exception ex) { LogUitl.Error(ex, "Api.RequestAuthenticationHandler"); } HttpResponseMessage response = await base.SendAsync(request, cancellationToken); return response; } private string TokenVerification(string token) { if (string.IsNullOrEmpty(token)) { return null; } string loginID = null; if (HttpRuntime.Cache[Constants.CacheKey_SessionTokenPrefix + token] == null) //若是過時,則嘗試從DB中恢復受權狀態 { using (var da = BaseUtil.CreateDataAccess()) { //loginID = 根據Token獲取登陸用戶ID邏輯 if (!string.IsNullOrEmpty(loginID)) { HttpRuntime.Cache.Insert(Constants.CacheKey_SessionTokenPrefix + token, loginID, null, Cache.NoAbsoluteExpiration, TimeSpan.FromHours(1)); } } } else { loginID = HttpRuntime.Cache[Constants.CacheKey_SessionTokenPrefix + token].ToNotNullString(); } return loginID; } }
6.HandleExceptionFilterAttribute(全局異常處理過濾器,只要某個ACTION發生異常就會報被該過濾器捕獲並處理)
/// <summary> /// 統一全局異常過濾處理 /// </summary> public class HandleExceptionFilterAttribute : ExceptionFilterAttribute { public override void OnException(HttpActionExecutedContext actionExecutedContext) { string ctrllerName = actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerName; string actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName; string sqlCmd = null; if (actionExecutedContext.ActionContext.ActionArguments.ContainsKey("sqlCmd")) { sqlCmd = BaseUtil.SerializeToJson(actionExecutedContext.ActionContext.ActionArguments["sqlCmd"] as SqlCmdInfo); } //記錄到日誌表中 LogUitl.Error(actionExecutedContext.Exception.Message, "Api.HandleExceptionFilterAttribute", string.Format("SqlCmdInfo:{0};StackTrace:{1}", sqlCmd, actionExecutedContext.Exception.StackTrace)); var errResult = new ApiResultInfo(false, sqlCmd, actionName + "Err", actionExecutedContext.Exception.Message); errResult.ExtendedData["StackTrace"] = actionExecutedContext.Exception.StackTrace; actionExecutedContext.Response = actionExecutedContext.ActionContext.Request.CreateResponse(HttpStatusCode.OK, errResult, "application/json"); } }
7.ApiResultInfo(API返回結果實體類)
[Serializable] public class ApiResultInfo { public bool Stauts { get; set; } public object Data { get; set; } public string ErrCode { get; set; } public string ErrMsg { get; set; } public Dictionary<string, object> ExtendedData { get; set; } public ApiResultInfo() { this.ExtendedData = new Dictionary<string, object>(); } public ApiResultInfo(bool status, object data = null, string errCode = null, string errMsg = null, Dictionary<string, object> extData = null) { this.Stauts = status; this.Data = data; this.ErrCode = errCode; this.ErrMsg = errMsg; this.ExtendedData = extData; if (this.ExtendedData == null) { this.ExtendedData = new Dictionary<string, object>(); } } /// <summary> /// 構建成功結果對象 /// </summary> /// <param name="data"></param> /// <param name="extData"></param> /// <returns></returns> public static ApiResultInfo BuildOKResult(object data = null, Dictionary<string, object> extData = null) { return new ApiResultInfo(true, data, extData: extData); } /// <summary> /// 構建錯誤結果對象 /// </summary> /// <param name="errCode"></param> /// <param name="errMsg"></param> /// <param name="extData"></param> /// <returns></returns> public static ApiResultInfo BuildErrResult(string errCode = null, string errMsg = null, Dictionary<string, object> extData = null) { return new ApiResultInfo(false, errCode: errCode, errMsg: errMsg, extData: extData); } }
8.非對稱加解密算法(容許客戶端請求時進行公鑰加密請求內容,而後服務端API中經過RequestAuthenticationHandler自定義驗證管道解密請求內容)
/// <summary> /// 生成公鑰及私鑰對 /// </summary> /// <param name="publickey"></param> /// <param name="privatekey"></param> public static void GeneratePublicAndPrivateKey(out string publickey, out string privatekey) { RSACryptoServiceProvider crypt = new RSACryptoServiceProvider(); publickey = crypt.ToXmlString(false);//公鑰 privatekey = crypt.ToXmlString(true);//私鑰 } /// <summary> /// 分段使用公鑰加密 /// </summary> /// <param name="publicKey"></param> /// <param name="rawInput"></param> /// <returns></returns> public static string RSAEncrypt(string publicKey, string rawInput) { if (string.IsNullOrEmpty(rawInput)) { return string.Empty; } if (string.IsNullOrWhiteSpace(publicKey)) { throw new ArgumentException("Invalid Public Key"); } using (var rsaProvider = new RSACryptoServiceProvider()) { var inputBytes = Encoding.UTF8.GetBytes(rawInput);//有含義的字符串轉化爲字節流 rsaProvider.FromXmlString(publicKey);//載入公鑰 int bufferSize = (rsaProvider.KeySize / 8) - 11;//單塊最大長度 var buffer = new byte[bufferSize]; using (MemoryStream inputStream = new MemoryStream(inputBytes), outputStream = new MemoryStream()) { while (true) { //分段加密 int readSize = inputStream.Read(buffer, 0, bufferSize); if (readSize <= 0) { break; } var temp = new byte[readSize]; Array.Copy(buffer, 0, temp, 0, readSize); var encryptedBytes = rsaProvider.Encrypt(temp, false); outputStream.Write(encryptedBytes, 0, encryptedBytes.Length); } return Convert.ToBase64String(outputStream.ToArray());//轉化爲字節流方便傳輸 } } } /// <summary> /// 分段使用私鑰解密 /// </summary> /// <param name="privateKey"></param> /// <param name="encryptedInput"></param> /// <returns></returns> public static string RSADecrypt(string privateKey, string encryptedInput) { if (string.IsNullOrEmpty(encryptedInput)) { return string.Empty; } if (string.IsNullOrWhiteSpace(privateKey)) { throw new ArgumentException("Invalid Private Key"); } using (var rsaProvider = new RSACryptoServiceProvider()) { var inputBytes = Convert.FromBase64String(encryptedInput); rsaProvider.FromXmlString(privateKey); int bufferSize = rsaProvider.KeySize / 8; var buffer = new byte[bufferSize]; using (MemoryStream inputStream = new MemoryStream(inputBytes), outputStream = new MemoryStream()) { while (true) { int readSize = inputStream.Read(buffer, 0, bufferSize); if (readSize <= 0) { break; } var temp = new byte[readSize]; Array.Copy(buffer, 0, temp, 0, readSize); var rawBytes = rsaProvider.Decrypt(temp, false); outputStream.Write(rawBytes, 0, rawBytes.Length); } return Encoding.UTF8.GetString(outputStream.ToArray()); } } }
9.LogUitl(基於NLOG.MONGO組件簡單封裝實現MONGODB日誌功能)--後期有機會再單獨講MONGODB的相關知識
public static class LogUitl { private static NLog.Logger _Logger = null; private const string cacheKey_NLogConfigFlag = "NLogConfigFlag"; private static Logger GetLogger() { if (_Logger == null || HttpRuntime.Cache[cacheKey_NLogConfigFlag] == null) { LoggingConfiguration config = new LoggingConfiguration(); string connSetStr = ConfigUtility.GetAppSettingValue("MongoDbConnectionSet"); MongoTarget mongoTarget = new MongoTarget(); mongoTarget.ConnectionString = EncryptUtility.Decrypt(connSetStr); mongoTarget.DatabaseName = "KYELog"; mongoTarget.CollectionName = "KYCallCenterLog"; mongoTarget.IncludeDefaults = false; AppendLogMongoFields(mongoTarget.Fields); LoggingRule rule1 = new LoggingRule("*", LogLevel.Debug, mongoTarget); config.LoggingRules.Add(rule1); LogManager.Configuration = config; _Logger = LogManager.GetCurrentClassLogger(); HttpRuntime.Cache.Insert(cacheKey_NLogConfigFlag, "Nlog", new System.Web.Caching.CacheDependency(HttpContext.Current.Server.MapPath("~/Web.config"))); } return _Logger; } private static void AppendLogMongoFields(IList<MongoField> mongoFields) { mongoFields.Clear(); Type logPropertiesType = typeof(SysLogInfo.LogProperties); foreach (var pro in typeof(SysLogInfo).GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (pro.PropertyType == logPropertiesType) continue; string layoutStr = string.Empty; //"${event-context:item=" + pro.Name + "}"; if (pro.Name.Equals("ThreadID") || pro.Name.Equals("Level") || pro.Name.Equals("MachineName")) { layoutStr = "${" + pro.Name.ToLower() + "}"; } else if (pro.Name.Equals("LogDT")) { layoutStr = "${date:format=yyyy-MM-dd HH\\:mm\\:ss}"; } else if (pro.Name.Equals("Msg")) { layoutStr = "${message}"; } if (!string.IsNullOrEmpty(layoutStr)) { mongoFields.Add(new MongoField(pro.Name, layoutStr, pro.PropertyType.Name)); } } } private static LogEventInfo BuildLogEventInfo(LogLevel level, string msg, string source, string detailTrace = null, string other1 = null, string other2 = null, string other3 = null) { var eventInfo = new LogEventInfo(); eventInfo.Level = level; eventInfo.Message = msg; eventInfo.Properties["DetailTrace"] = detailTrace ?? string.Empty; eventInfo.Properties["Source"] = source ?? string.Empty; eventInfo.Properties["Other1"] = other1 ?? string.Empty; eventInfo.Properties["Other2"] = other2 ?? string.Empty; eventInfo.Properties["Other3"] = other3 ?? string.Empty; string uid = string.Empty; if (HttpContext.Current.User != null) { uid = HttpContext.Current.User.Identity.Name; } eventInfo.Properties["UserID"] = uid; return eventInfo; } public static void Info(string msg, string source, string detailTrace = null, string other1 = null, string other2 = null, string other3 = null) { try { var eventInfo = BuildLogEventInfo(LogLevel.Info, msg, source, detailTrace, other1, other2, other3); var logger = GetLogger(); logger.Log(eventInfo); } catch { } } public static void Warn(string msg, string source, string detailTrace = null, string other1 = null, string other2 = null, string other3 = null) { try { var eventInfo = BuildLogEventInfo(LogLevel.Warn, msg, source, detailTrace, other1, other2, other3); var logger = GetLogger(); logger.Log(eventInfo); } catch { } } public static void Error(string msg, string source, string detailTrace = null, string other1 = null, string other2 = null, string other3 = null) { try { var eventInfo = BuildLogEventInfo(LogLevel.Error, msg, source, detailTrace, other1, other2, other3); var logger = GetLogger(); logger.Log(eventInfo); } catch { } } public static void Error(Exception ex, string source, string other1 = null, string other2 = null, string other3 = null) { try { var eventInfo = BuildLogEventInfo(LogLevel.Error, ex.Message, source, ex.StackTrace, other1, other2, other3); var logger = GetLogger(); logger.Log(eventInfo); } catch { } } } public class SysLogInfo { public DateTime LogDT { get; set; } public int ThreadID { get; set; } public string Level { get; set; } public string Msg { get; set; } public string MachineName { get; set; } public LogProperties Properties { get; set; } public class LogProperties { public string Source { get; set; } public string DetailTrace { get; set; } public string UserID { get; set; } public string Other1 { get; set; } public string Other2 { get; set; } public string Other3 { get; set; } } }
10.其它一些用到的公共實用方法
//BaseUtil: public static DataAccess CreateDataAccess(string connName = "DefaultConnectionString") { return new DataAccess(connName, EncryptUtility.Decrypt); } public static string SerializeToJson(object obj) { return JsonConvert.SerializeObject(obj); } public static JObject DeserializeObject(string json) { return JObject.Parse(json); } public static T DeserializeObject<T>(string json) { return JsonConvert.DeserializeObject<T>(json); } //===================================== /// <summary> /// 類型擴展方法集合 /// </summary> public static class TypeExtension { /// <summary> /// 轉換爲不爲空的字符串(即:若爲空,則返回爲空字符串,而不是Null) /// </summary> /// <param name="obj"></param> /// <returns></returns> public static string ToNotNullString(this object obj) { if (obj == null || obj == DBNull.Value) { return string.Empty; } return obj.ToString(); } /// <summary> /// 判斷列表中是否存在項 /// </summary> /// <param name="list"></param> /// <returns></returns> public static bool HasItem(this IEnumerable<object> list) { if (list != null && list.Any()) { return true; } return false; } /// <summary> /// 從字答串左邊起取出指定長度的字符串 /// </summary> /// <param name="str"></param> /// <param name="length"></param> /// <returns></returns> public static string Left(this string str, int length) { if (string.IsNullOrEmpty(str)) { return string.Empty; } return str.Substring(0, length); } /// <summary> /// 從字答串右邊起取出指定長度的字符串 /// </summary> /// <param name="str"></param> /// <param name="length"></param> /// <returns></returns> public static string Right(this string str, int length) { if (string.IsNullOrEmpty(str)) { return string.Empty; } return str.Substring(str.Length - length); } /// <summary> /// 判斷DataSet指定表是否包含記錄 /// </summary> /// <param name="ds"></param> /// <param name="tableIndex"></param> /// <returns></returns> public static bool HasRows(this DataSet ds, int tableIndex = 0) { if (ds != null && ds.Tables[tableIndex].Rows.Count > 0) { return true; } else { return false; } } /// <summary> /// 通用類型轉換方法,EG:"".As<String>() /// </summary> /// <typeparam name="T"></typeparam> /// <param name="obj"></param> /// <returns></returns> public static T As<T>(this object obj) { T result; try { Type type = typeof(T); if (type.IsNullableType()) { if (obj == null || obj.ToString().Length == 0) { result = default(T); } else { type = type.GetGenericArguments()[0]; result = (T)Convert.ChangeType(obj, type); } } else { if (obj == null) { if (type == typeof(string)) { result = (T)Convert.ChangeType(string.Empty, type); } else { result = default(T); } } else { result = (T)Convert.ChangeType(obj, type); } } } catch { result = default(T); } return result; } /// <summary> /// 判斷是否爲可空類型 /// </summary> /// <param name="type"></param> /// <returns></returns> public static bool IsNullableType(this Type type) { return (type.IsGenericType && type.GetGenericTypeDefinition().Equals (typeof(Nullable<>))); } /// <summary> /// 嘗試將ArrayList轉換爲Array,若是爲空則轉換爲null /// </summary> /// <param name="arrList"></param> /// <returns></returns> public static object[] TryToArray(this ArrayList arrList) { return arrList != null ? arrList.ToArray() : null; } }
WebApiConfig增長上述定義的一些過濾器、處理管道,以便實現攔截處理:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API 配置和服務 // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } ); config.Filters.Add(new HandleExceptionFilterAttribute());//添加統一處理異常過濾器 config.MessageHandlers.Add(new RequestAuthenticationHandler());//添加統一TOKEN身份證碼 GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new DefaultContractResolver { IgnoreSerializableAttribute = true }; } }
客戶端調用上述API相關核心代碼:
1.DataService(客戶端訪問API通用類,經過該類公共靜態方法能夠進行數據庫的CRUD)
public class DataService : BaseService { #region 私有方法 private static SqlCmdInfo BuildSqlCmdInfo(string sqlCmdText, bool isSPCmdType = false, int dbType = 0, params object[] sqlParams) { var sqlCmdInfo = new SqlCmdInfo() { SqlCmdText = sqlCmdText, DbType = dbType, IsSPCmdType = isSPCmdType }; if (sqlParams != null && sqlParams.Length > 0) { sqlCmdInfo.Parameters = new ArrayList(sqlParams); } return sqlCmdInfo; } private static string GetRrequestApiUrl(string action) { string requestApiUrl = string.Format("http://{0}/api/Data/{1}", ApiHost, action); return requestApiUrl; } #endregion public static ApiResultInfo<string> Login(string uid, string pwd, string mac, string pcName) { var result = WebApiUtil.GetResultFromWebApi<string>(null, new[] { uid, pwd, mac, pcName }, GetRrequestApiUrl("Login")); if (result.Stauts) { SessionToken = result.Data; } return result; } public static void LogOut() { WebApiUtil.GetResultFromWebApi<string>(AddHeadersWithToken(), string.Format("{\"\":\"{0}\"}", SessionToken), GetRrequestApiUrl("LogOut")); } public static T GetValue<T>(string sqlCmdText, object[] sqlParams = null, bool isSPCmdType = false, int dbType = 0) { var sqlCmdInfo = BuildSqlCmdInfo(sqlCmdText, isSPCmdType, dbType, sqlParams); var result = WebApiUtil.GetResultFromWebApi<T>(AddHeadersWithToken(), sqlCmdInfo, GetRrequestApiUrl("GetValue")); if (result.Stauts) { return result.Data; } throw new Exception(result.ErrCode + ":" + result.ErrMsg); } public static DataSet GetDataSet(string sqlCmdText, object[] sqlParams = null, bool isSPCmdType = false, int dbType = 0) { var sqlCmdInfo = BuildSqlCmdInfo(sqlCmdText, isSPCmdType, dbType, sqlParams); var result = WebApiUtil.GetResultFromWebApi<DataSet>(AddHeadersWithToken(), sqlCmdInfo, GetRrequestApiUrl("GetDataSet")); if (result.Stauts) { return result.Data; } throw new Exception(result.ErrCode + ":" + result.ErrMsg); } public static DataTable GetDataTable(string sqlCmdText, object[] sqlParams = null, bool isSPCmdType = false, int dbType = 0) { var sqlCmdInfo = BuildSqlCmdInfo(sqlCmdText, isSPCmdType, dbType, sqlParams); var result = WebApiUtil.GetResultFromWebApi<DataTable>(AddHeadersWithToken(), sqlCmdInfo, GetRrequestApiUrl("GetDataTable")); if (result.Stauts) { return result.Data; } throw new Exception(result.ErrCode + ":" + result.ErrMsg); } public static int ExecuteCommand(string sqlCmdText, object[] sqlParams = null, bool isSPCmdType = false, int dbType = 0) { var sqlCmdInfo = BuildSqlCmdInfo(sqlCmdText, isSPCmdType, dbType, sqlParams); var result = WebApiUtil.GetResultFromWebApi<int>(AddHeadersWithToken(), sqlCmdInfo, GetRrequestApiUrl("ExecuteCommand")); if (result.Stauts) { return result.Data; } throw new Exception(result.ErrCode + ":" + result.ErrMsg); } public static bool BatchExecuteCommand(IEnumerable<SqlCmdInfo> sqlCmdInfos) { var result = WebApiUtil.GetResultFromWebApi<bool>(AddHeadersWithToken(), sqlCmdInfos, GetRrequestApiUrl("BatchExecuteCommand")); if (result.Stauts) { return result.Data; } throw new Exception(result.ErrCode + ":" + result.ErrMsg); } public static void ExecuteCommandAsync(string sqlCmdText, object[] sqlParams = null, bool isSPCmdType = false, int dbType = 0, Action<ApiResultInfo<object>> callBackAction = null) { var sqlCmdInfo = BuildSqlCmdInfo(sqlCmdText, isSPCmdType, dbType, sqlParams); Func<SqlCmdInfo, ApiResultInfo<object>> execCmdFunc = new Func<SqlCmdInfo, ApiResultInfo<object>>((sqlCmdObj) => { var result = WebApiUtil.GetResultFromWebApi<object>(AddHeadersWithToken(), sqlCmdObj, GetRrequestApiUrl("ExecuteCommandAsync")); return result; }); execCmdFunc.BeginInvoke(sqlCmdInfo, new AsyncCallback((ar) => { ApiResultInfo<object> apiResult = null; try { var func = ar.AsyncState as Func<SqlCmdInfo, ApiResultInfo<object>>; apiResult = func.EndInvoke(ar); } catch (Exception ex) { apiResult = new ApiResultInfo<object>(false, ex, "ExecuteCommandAsyncErr", ex.Message); } if (callBackAction != null) { callBackAction(apiResult); } }), execCmdFunc); } public static void SaveLogAsync(string logType, string msg, string source, string detailTrace = null, string other1 = null, string other2 = null, string other3 = null) { string[] logInfo = new[] { logType, msg, source, detailTrace, other1, other2, other3 }; Task.Factory.StartNew((o) => { try { string[] logInfoObj = o as string[]; var result = WebApiUtil.HttpRequestToString(AddHeadersWithToken(), logInfoObj, GetRrequestApiUrl("SaveLog")); } catch { } }, logInfo); } } public abstract class BaseService { public static string SessionToken = null; public static string ApiHost = null; protected static Dictionary<string, string> AddHeadersWithToken() { return new Dictionary<string, string> { {"AccessToken",SessionToken} }; } }
2.WebApiUtil(WEB API請求工具類)
/// <summary> /// WebApi實用工具類 /// Author:Zuowenjun /// Date:2017/11/3 /// </summary> public static class WebApiUtil { private const string rsaPublicKey = "公鑰字符串"; static WebApiUtil() { System.Net.ServicePointManager.DefaultConnectionLimit = 512; } /// <summary> /// 獲取API結果 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="requestHeaders"></param> /// <param name="requestMsg"></param> /// <param name="apiUrl"></param> /// <param name="requestMethod"></param> /// <returns></returns> public static ApiResultInfo<T> GetResultFromWebApi<T>(Dictionary<string, string> requestHeaders, object requestMsg, string apiUrl, string requestMethod = "POST") { string retString = HttpRequestToString(requestHeaders, requestMsg, apiUrl, requestMethod); return JsonConvert.DeserializeObject<ApiResultInfo<T>>(retString); } /// <summary> /// 發送Http請求,模擬訪問指定的Url,返回響應內容轉換成JSON對象 /// </summary> /// <param name="requestHeaders"></param> /// <param name="requestMsg"></param> /// <param name="apiUrl"></param> /// <param name="requestMethod"></param> /// <returns></returns> public static JObject HttpRequestToJson(Dictionary<string, string> requestHeaders, object requestMsg, string apiUrl, string requestMethod = "POST") { string retString = HttpRequestToString(requestHeaders, requestMsg, apiUrl, requestMethod); return JObject.Parse(retString); } /// <summary> /// 發送Http請求,模擬訪問指定的Url,返回響應內容文本 /// </summary> /// <param name="requestHeaders">請求頭</param> /// <param name="requestData">請求體(若爲GetMethod時,則該值應爲空)</param> /// <param name="apiUrl">要訪問的Url</param> /// <param name="requestMethod">請求方式</param> /// <param name="isEncryptBody">是否對請求體內容進行公鑰加密</param> /// <returns>響應內容(響應頭、響應體)</returns> public static string HttpRequestToString(Dictionary<string, string> requestHeaders, object requestMsg, string apiUrl, string requestMethod = "POST", bool isEncryptBody = true) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiUrl); request.Method = requestMethod; request.KeepAlive = false; request.Proxy = null; request.ServicePoint.UseNagleAlgorithm = false; request.AllowWriteStreamBuffering = false; request.ContentType = "application/json"; if (requestHeaders != null) { foreach (var item in requestHeaders) { request.Headers.Add(item.Key, item.Value); } } request.Headers.Set("Pragma", "no-cache"); if (requestMsg != null) { string dataStr = JsonConvert.SerializeObject(requestMsg); if (isEncryptBody) { request.Headers.Add("Encryption", "1"); dataStr = RSAEncrypt(rsaPublicKey, dataStr);//加密請求內容 } byte[] data = Encoding.UTF8.GetBytes(dataStr); request.ContentLength = data.Length; using (Stream myRequestStream = request.GetRequestStream()) { myRequestStream.Write(data, 0, data.Length); myRequestStream.Close(); } } string retString = null; using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { retString = GetResponseBody(response); } request = null; return retString; } private static string GetResponseBody(HttpWebResponse response) { string responseBody = string.Empty; if (response.ContentEncoding.ToLower().Contains("gzip")) { using (GZipStream stream = new GZipStream(response.GetResponseStream(), CompressionMode.Decompress)) { using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) { responseBody = reader.ReadToEnd(); } } } else if (response.ContentEncoding.ToLower().Contains("deflate")) { using (DeflateStream stream = new DeflateStream( response.GetResponseStream(), CompressionMode.Decompress)) { using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) { responseBody = reader.ReadToEnd(); } } } else { using (Stream stream = response.GetResponseStream()) { using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) { responseBody = reader.ReadToEnd(); } } } return responseBody; } public static string RSAEncrypt(string publicKey, string rawInput) { if (string.IsNullOrEmpty(rawInput)) { return string.Empty; } if (string.IsNullOrWhiteSpace(publicKey)) { throw new ArgumentException("Invalid Public Key"); } using (var rsaProvider = new RSACryptoServiceProvider()) { var inputBytes = Encoding.UTF8.GetBytes(rawInput);//有含義的字符串轉化爲字節流 rsaProvider.FromXmlString(publicKey);//載入公鑰 int bufferSize = (rsaProvider.KeySize / 8) - 11;//單塊最大長度 var buffer = new byte[bufferSize]; using (MemoryStream inputStream = new MemoryStream(inputBytes), outputStream = new MemoryStream()) { while (true) { //分段加密 int readSize = inputStream.Read(buffer, 0, bufferSize); if (readSize <= 0) { break; } var temp = new byte[readSize]; Array.Copy(buffer, 0, temp, 0, readSize); var encryptedBytes = rsaProvider.Encrypt(temp, false); outputStream.Write(encryptedBytes, 0, encryptedBytes.Length); } return Convert.ToBase64String(outputStream.ToArray());//轉化爲字節流方便傳輸 } } } }
3.ApiResultInfo(API返回結果類)、SqlCmdInfo(SQL命令信息類) 與服務端的同名類基本相同,只是少了一些方法,由於這些方法在客戶端用不到全部無需再定義了:
[Serializable] public class SqlCmdInfo { public string SqlCmdText { get; set; } public ArrayList Parameters { get; set; } public bool IsSPCmdType { get; set; } public int DbType { get; set; } } [Serializable] public class ApiResultInfo<T> { public bool Stauts { get; set; } public T Data { get; set; } public string ErrCode { get; set; } public string ErrMsg { get; set; } public Dictionary<string, object> ExtendedData { get; set; } public ApiResultInfo() { this.ExtendedData = new Dictionary<string, object>(); } public ApiResultInfo(bool status, T data, string errCode = null, string errMsg = null, Dictionary<string, object> extData = null) { this.Stauts = status; this.Data = data; this.ErrCode = errCode; this.ErrMsg = errMsg; this.ExtendedData = extData; if (this.ExtendedData == null) { this.ExtendedData = new Dictionary<string, object>(); } } }
客戶端使用方法以下:
//直接使用DataService的相關公共靜態方法便可,就像本地直接使用ADO.NET操做數據庫同樣;例如: DataTable dt = DataService.GetDataTable("SQL語句", new object[] { 參數}); DataService.ExecuteCommand("SQL語句", new Object[] { 參數 });
以上就是基於ASP.NET WEB API實現分佈式數據訪問中間層,這個分佈式數據訪問中間層雖簡單,但我包含了以下幾個核心內容:
1.身份驗證:未經登陸受權是沒法訪問API,固然上述的驗證很是簡單,這個能夠根據實際狀況進行擴展,好比:OA2.0驗證,摘要驗證等
2.請求內容加密:一些重要的請求內容必須要加密,並且防止被破解,這裏使用公鑰加密,私鑰解密,從客戶端是沒法截獲加密的請求內容
3.壓縮響應報文:若是返回的內容過大,則須要進行壓縮,以提升響應速度
4.防SQL注入:經過自定義過濾器,對傳入的SQL語句利用正則進行分析,若包含非法關鍵字則直接不予執行且報錯,執行SQL語句時也是全面採用ADO.NET的參數化,確保整個執行安全可靠。
5.全局異常捕獲:對於每一個可能發生的異常一個都不放過,所有記錄到日誌中
6.MONGODB日誌記錄:利用NLOG.MONGO組件實現日誌記錄,不影響正常的DB
C# 實現AOP 的幾種常見方式
AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,經過預編譯方式和運行期動態代理實現程序功能的中統一處理業務邏輯的一種技術,比較常見的場景是:日誌記錄,錯誤捕獲、性能監控等
AOP的本質是經過代理對象來間接執行真實對象,在代理類中每每會添加裝飾一些額外的業務代碼,好比以下代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class
RealA
{
public
virtual
string
Pro {
get
;
set
; }
public
virtual
void
ShowHello(
string
name)
{
Console.WriteLine($
"Hello!{name},Welcome!"
);
}
}
//調用:
var
a =
new
RealA();
a.Pro =
"測試"
;
a.ShowHello(
"夢在旅途"
);
|
這段代碼很簡單,只是NEW一個對象,而後設置屬性及調用方法,但若是我想在設置屬性先後及調用方法先後或報錯都能收集日誌信息,該如何作呢?可能你們會想到,在設置屬性及調用方法先後都加上記錄日誌的代碼不就能夠了,雖然這樣是能夠,但若是不少地方都要用到這個類的時候,那重複的代碼是否太多了一些吧,因此咱們應該使用代理模式或裝飾模式,將原有的真實類RealA委託給代理類ProxyRealA來執行,代理類中在設置屬性及調用方法時,再添加記錄日誌的代碼就能夠了,這樣能夠保證代碼的乾淨整潔,也便於代碼的後期維護。(注意,在C#中若需被子類重寫,父類必需是虛方法或虛屬性virtual)
以下代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
class
ProxyRealA : RealA
{
public
override
string
Pro
{
get
{
return
base
.Pro;
}
set
{
ShowLog(
"設置Pro屬性前日誌信息"
);
base
.Pro = value;
ShowLog($
"設置Pro屬性後日志信息:{value}"
);
}
}
public
override
void
ShowHello(
string
name)
{
try
{
ShowLog(
"ShowHello執行前日誌信息"
);
base
.ShowHello(name);
ShowLog(
"ShowHello執行後日志信息"
);
}
catch
(Exception ex)
{
ShowLog($
"ShowHello執行出錯日誌信息:{ex.Message}"
);
}
}
private
void
ShowLog(
string
log)
{
Console.WriteLine($
"{DateTime.Now.ToString()}-{log}"
);
}
}
//調用:
var
aa =
new
ProxyRealA();
aa.Pro =
"測試2"
;
aa.ShowHello(
"zuowenjun.cn"
);
|
這段代碼一樣很簡單,就是ProxyRealA繼承自RealA類,便可當作是ProxyRealA代理RealA,由ProxyRealA提供各類屬性及方法調用。這樣在ProxyRealA類內部屬性及方法執行先後都有統一記錄日誌的代碼,不論在哪裏用這個RealA類,均可以直接用ProxyRealA類代替,由於里氏替換原則,父類能夠被子類替換,並且後續若想更改日誌記錄代碼方式,只須要在ProxyRealA中更改就好了,這樣全部用到的ProxyRealA類的日誌都會改變,是否是很爽。上述執行結果以下圖示:
以上經過定義代理類的方式可以實如今方法中統一進行各類執行點的攔截代碼邏輯處理,攔截點(或者稱爲:橫切面,切面點)通常主要爲:執行前,執行後,發生錯誤,雖然解決了以前直接調用真實類RealA時,須要重複增長各類邏輯代碼的問題,但隨之而來的新問題又來了,那就是當一個系統中的類很是多的時候,若是咱們針對每一個類都定義一個代理類,那麼系統的類的個數會成倍增長,並且不一樣的代理類中可能某些攔截業務邏輯代碼都是相同的,這種狀況一樣是不能容許的,那有沒有什麼好的辦法呢?答案是確定的,如下是我結合網上資源及我的總結的以下幾種常見的實現AOP的方式,各位能夠參考學習。
第一種:靜態織入,即:在編譯時,就將各類涉及AOP攔截的代碼注入到符合必定規則的類中,編譯後的代碼與咱們直接在RealA調用屬性或方法先後增長代碼是相同的,只是這個工做交由編譯器來完成。
PostSharp:PostSharp的Aspect是使用Attribute實現的,咱們只需事先經過繼承自OnMethodBoundaryAspect,而後重寫幾個常見的方法便可,如:OnEntry,OnExit等,最後只須要在須要進行AOP攔截的屬性或方法上加上AOP攔截特性類便可。因爲PostSharp是靜態織入的,因此相比其它的經過反射或EMIT反射來講效率是最高的,但PostSharp是收費版本的,並且網上的教程比較多,我就不在此重複說明了,你們能夠參見:使用PostSharp在.NET平臺上實現AOP
第二種:EMIT反射,即:經過Emit反射動態生成代理類,以下Castle.DynamicProxy的AOP實現方式,代碼也仍是比較簡單的,效率相對第一種要慢一點,但對於普通的反射來講又高一些,代碼實現以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
using
Castle.Core.Interceptor;
using
Castle.DynamicProxy;
using
NLog;
using
NLog.Config;
using
NLog.Win32.Targets;
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
System.Threading.Tasks;
namespace
ConsoleApp
{
class
Program
{
static
void
Main(
string
[] args)
{
ProxyGenerator generator =
new
ProxyGenerator();
var
test = generator.CreateClassProxy<TestA>(
new
TestInterceptor());
Console.WriteLine($
"GetResult:{test.GetResult(Console.ReadLine())}"
);
test.GetResult2(
"test"
);
Console.ReadKey();
}
}
public
class
TestInterceptor : StandardInterceptor
{
private
static
NLog.Logger logger;
protected
override
void
PreProceed(IInvocation invocation)
{
Console.WriteLine(invocation.Method.Name +
"執行前,入參:"
+
string
.Join(
","
, invocation.Arguments));
}
protected
override
void
PerformProceed(IInvocation invocation)
{
Console.WriteLine(invocation.Method.Name +
"執行中"
);
try
{
base
.PerformProceed(invocation);
}
catch
(Exception ex)
{
HandleException(ex);
}
}
protected
override
void
PostProceed(IInvocation invocation)
{
Console.WriteLine(invocation.Method.Name +
"執行後,返回值:"
+ invocation.ReturnValue);
}
private
void
HandleException(Exception ex)
{
if
(logger ==
null
)
{
LoggingConfiguration config =
new
LoggingConfiguration();
ColoredConsoleTarget consoleTarget =
new
ColoredConsoleTarget();
consoleTarget.Layout =
"${date:format=HH\\:MM\\:ss} ${logger} ${message}"
;
config.AddTarget(
"console"
, consoleTarget);
LoggingRule rule1 =
new
LoggingRule(
"*"
, LogLevel.Debug, consoleTarget);
config.LoggingRules.Add(rule1);
LogManager.Configuration = config;
logger = LogManager.GetCurrentClassLogger();
//new NLog.LogFactory().GetCurrentClassLogger();
}
logger.ErrorException(
"error"
,ex);
}
}
public
class
TestA
{
public
virtual
string
GetResult(
string
msg)
{
string
str = $
"{DateTime.Now.ToString("
yyyy-mm-dd HH:mm:ss
")}---{msg}"
;
return
str;
}
public
virtual
string
GetResult2(
string
msg)
{
throw
new
Exception(
"throw Exception!"
);
}
}
}
|
簡要說明一下代碼原理,先建立ProxyGenerator類實例,從名字就看得出來,是代理類生成器,而後實例化一個基於繼承自StandardInterceptor的TestInterceptor,這個TestInterceptor是一個自定義的攔截器,最後經過generator.CreateClassProxy<TestA>(new TestInterceptor())動態建立了一個繼承自TestA的動態代理類,這個代理類只有在運行時纔會生成的,後面就能夠如代碼所示,直接用動態代理類對象實例Test操做TestA的全部屬性與方法,固然這裏須要注意,若須要被動態代理類所代理並攔截,則父類的屬性或方法必需是virtual,這點與我上面說的直接寫一個代理類相同。
上述代碼運行效果以下:
第三種:普通反射+利用Remoting的遠程訪問對象時的直實代理類來實現,代碼以下,這個可能相比以上兩種稍微複雜一點:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Runtime.Remoting.Activation;
using
System.Runtime.Remoting.Messaging;
using
System.Runtime.Remoting.Proxies;
using
System.Text;
using
System.Threading.Tasks;
namespace
ConsoleApp
{
class
Program
{
static
void
Main(
string
[] args)
{
var
A =
new
AopClass();
A.Hello();
var
aop =
new
AopClassSub(
"夢在旅途"
);
aop.Pro =
"test"
;
aop.Output(
"hlf"
);
aop.ShowMsg();
Console.ReadKey();
}
}
[AopAttribute]
public
class
AopClass : ContextBoundObject
{
public
string
Hello()
{
return
"welcome"
;
}
}
public
class
AopClassSub : AopClass
{
public
string
Pro =
null
;
private
string
Msg =
null
;
public
AopClassSub(
string
msg)
{
Msg = msg;
}
public
void
Output(
string
name)
{
Console.WriteLine(name +
",你好!-->P:"
+ Pro);
}
public
void
ShowMsg()
{
Console.WriteLine($
"構造函數傳的Msg參數內容是:{Msg}"
);
}
}
public
class
AopAttribute : ProxyAttribute
{
public
override
MarshalByRefObject CreateInstance(Type serverType)
{
AopProxy realProxy =
new
AopProxy(serverType);
return
realProxy.GetTransparentProxy()
as
MarshalByRefObject;
}
}
public
class
AopProxy : RealProxy
{
public
AopProxy(Type serverType)
:
base
(serverType) { }
public
override
IMessage Invoke(IMessage msg)
{
if
(msg
is
IConstructionCallMessage)
{
IConstructionCallMessage constructCallMsg = msg
as
IConstructionCallMessage;
IConstructionReturnMessage constructionReturnMessage =
this
.InitializeServerObject((IConstructionCallMessage)msg);
RealProxy.SetStubData(
this
, constructionReturnMessage.ReturnValue);
Console.WriteLine(
"Call constructor"
);
return
constructionReturnMessage;
}
else
{
IMethodCallMessage callMsg = msg
as
IMethodCallMessage;
IMessage message;
try
{
Console.WriteLine(callMsg.MethodName +
"執行前。。。"
);
object
[] args = callMsg.Args;
object
o = callMsg.MethodBase.Invoke(GetUnwrappedServer(), args);
Console.WriteLine(callMsg.MethodName +
"執行後。。。"
);
message =
new
ReturnMessage(o, args, args.Length, callMsg.LogicalCallContext, callMsg);
}
catch
(Exception e)
{
message =
new
ReturnMessage(e, callMsg);
}
Console.WriteLine(message.Properties[
"__Return"
]);
return
message;
}
}
}
}
|
以上代碼實現步驟說明:
1.這裏定義的一個真實類AopClass必需繼承自ContextBoundObject類,而ContextBoundObject類又直接繼承自MarshalByRefObject類,代表該類是上下文綁定對象,容許在支持遠程處理的應用程序中跨應用程序域邊界訪問對象,說白了就是能夠獲取這個真實類的全部信息,以即可以被生成動態代理。
2.定義繼承自ProxyAttribute的代理特性標識類AopAttribute,以代表哪些類能夠被代理,同時注意重寫CreateInstance方法,在CreateInstance方法裏實現經過委託與生成透明代理類的過程,realProxy.GetTransparentProxy() 很是重要,目的就是根據定義的AopProxy代理類獲取生成透明代理類對象實例。
3.實現通用的AopProxy代理類,代理類必需繼承自RealProxy類,在這個代理類裏面重寫Invoke方法,該方法是統一執行被代理的真實類的全部方法、屬性、字段的出入口,咱們只須要在該方法中根據傳入的IMessage進行判斷並實現相應的攔截代碼便可。
4.最後在須要進行Aop攔截的類上標註AopAttribute便可(注意:被標識的類必需是如第1條說明的繼承自ContextBoundObject類),在實際調用的過程當中是感知不到任何的變化。且AopAttribute能夠被子類繼承,也就意味着全部子類均可以被代理並攔截。
如上代碼運行效果以下:
這裏順便分享微軟官方若是利用RealProxy類實現AOP的,詳見地址:https://msdn.microsoft.com/zh-cn/library/dn574804.aspx
第四種:反射+ 經過定義統一的出入口,並運用一些特性實現AOP的效果,好比:常見的MVC、WEB API中的過濾器特性 ,我這裏根據MVC的思路,實現了相似的MVC過濾器的AOP效果,只是中間用到了反射,可能性能不佳,但效果仍是成功實現了各類攔截,正如MVC同樣,既支持過濾器特性,也支持Controller中的Action執行前,執行後,錯誤等方法實現攔截
實現思路以下:
A.過濾器及Controller特定方法攔截實現原理:
1.獲取程序集中全部繼承自Controller的類型;
2.根據Controller的名稱找到第1步中的對應的Controller的類型:FindControllerType
3.根據找到的Controller類型及Action的名稱找到對應的方法:FindAction
4.建立Controller類型的實例;
5.根據Action方法找到定義在方法上的全部過濾器特性(包含:執行前、執行後、錯誤)
6.執行Controller中的OnActionExecuting方法,隨後執行執行前的過濾器特性列表,如:ActionExecutingFilter
7.執行Action方法,得到結果;
8.執行Controller中的OnActionExecuted方法,隨後執行執行後的過濾器特性列表,如:ActionExecutedFilter
9.經過try catch在catch中執行Controller中的OnActionError方法,隨後執行錯誤過濾器特性列表,如:ActionErrorFilter
10.最後返回結果;
B.實現執行路由配置效果原理:
1.增長可設置路由模板列表方法:AddExecRouteTemplate,在方法中驗證controller、action,並獲取模板中的佔位符數組,最後保存到類全局對象中routeTemplates;
2.增長根據執行路由執行對應的Controller中的Action方法的效果: Run,在該方法中主要遍歷全部路由模板,而後與實行執行的請求路由信息經過正則匹配,若匹配OK,並能正確找到Controller及Action,則說明正確,並最終統一調用:Process方法,執行A中的全部步驟最終返回結果。
須要說明該模擬MVC方案並無實現Action方法參數的的綁定功能,由於ModelBinding自己就是比較複雜的機制,因此這裏只是爲了搞清楚AOP的實現原理,故不做這方面的研究,你們若是有空能夠實現,最終實現MVC不只是ASP.NET MVC,還能夠是 Console MVC,甚至是Winform MVC等。
如下是實現的所有代碼,代碼中我已進行了一些基本的優化,能夠直接使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
|
public
abstract
class
Controller
{
public
virtual
void
OnActionExecuting(MethodInfo action)
{
}
public
virtual
void
OnActionExecuted(MethodInfo action)
{
}
public
virtual
void
OnActionError(MethodInfo action, Exception ex)
{
}
}
public
abstract
class
FilterAttribute : Attribute
{
public
abstract
string
FilterType {
get
; }
public
abstract
void
Execute(Controller ctrller,
object
extData);
}
public
class
ActionExecutingFilter : FilterAttribute
{
public
override
string
FilterType =>
"BEFORE"
;
public
override
void
Execute(Controller ctrller,
object
extData)
{
Console.WriteLine($
"我是在{ctrller.GetType().Name}.ActionExecutingFilter中攔截髮出的消息!-{DateTime.Now.ToString()}"
);
}
}
public
class
ActionExecutedFilter : FilterAttribute
{
public
override
string
FilterType =>
"AFTER"
;
public
override
void
Execute(Controller ctrller,
object
extData)
{
Console.WriteLine($
"我是在{ctrller.GetType().Name}.ActionExecutedFilter中攔截髮出的消息!-{DateTime.Now.ToString()}"
);
}
}
public
class
ActionErrorFilter : FilterAttribute
{
public
override
string
FilterType =>
"EXCEPTION"
;
public
override
void
Execute(Controller ctrller,
object
extData)
{
Console.WriteLine($
"我是在{ctrller.GetType().Name}.ActionErrorFilter中攔截髮出的消息!-{DateTime.Now.ToString()}-Error Msg:{(extData as Exception).Message}"
);
}
}
public
class
AppContext
{
private
static
readonly
Type ControllerType =
typeof
(Controller);
private
static
readonly
Dictionary<
string
, Type> matchedControllerTypes =
new
Dictionary<
string
, Type>();
private
static
readonly
Dictionary<
string
, MethodInfo> matchedControllerActions =
new
Dictionary<
string
, MethodInfo>();
private
Dictionary<
string
,
string
[]> routeTemplates =
new
Dictionary<
string
,
string
[]>();
public
void
AddExecRouteTemplate(
string
execRouteTemplate)
{
if
(!Regex.IsMatch(execRouteTemplate,
"{controller}"
, RegexOptions.IgnoreCase))
{
throw
new
ArgumentException(
"執行路由模板不正確,缺乏{controller}"
);
}
if
(!Regex.IsMatch(execRouteTemplate,
"{action}"
, RegexOptions.IgnoreCase))
{
throw
new
ArgumentException(
"執行路由模板不正確,缺乏{action}"
);
}
string
[] keys = Regex.Matches(execRouteTemplate,
@"(?<={)\w+(?=})"
, RegexOptions.IgnoreCase).Cast<Match>().Select(c => c.Value.ToLower()).ToArray();
routeTemplates.Add(execRouteTemplate,keys);
}
public
object
Run(
string
execRoute)
{
//{controller}/{action}/{id}
string
ctrller =
null
;
string
actionName =
null
;
ArrayList args =
null
;
Type controllerType =
null
;
bool
findResult =
false
;
foreach
(
var
r
in
routeTemplates)
{
string
[] keys = r.Value;
string
execRoutePattern = Regex.Replace(r.Key,
@"{(?<key>\w+)}"
, (m) =>
string
.Format(
@"(?<{0}>.[^/\\]+)"
, m.Groups[
"key"
].Value.ToLower()), RegexOptions.IgnoreCase);
args =
new
ArrayList();
if
(Regex.IsMatch(execRoute, execRoutePattern))
{
var
match = Regex.Match(execRoute, execRoutePattern);
for
(
int
i = 0; i < keys.Length; i++)
{
if
(
"controller"
.Equals(keys[i], StringComparison.OrdinalIgnoreCase))
{
ctrller = match.Groups[
"controller"
].Value;
}
else
if
(
"action"
.Equals(keys[i], StringComparison.OrdinalIgnoreCase))
{
actionName = match.Groups[
"action"
].Value;
}
else
{
args.Add(match.Groups[keys[i]].Value);
}
}
if
((controllerType = FindControllerType(ctrller)) !=
null
&& FindAction(controllerType, actionName, args.ToArray()) !=
null
)
{
findResult =
true
;
break
;
}
}
}
if
(findResult)
{
return
Process(ctrller, actionName, args.ToArray());
}
else
{
throw
new
Exception($
"在已配置的路由模板列表中未找到與該執行路由相匹配的路由信息:{execRoute}"
);
}
}
public
object
Process(
string
ctrller,
string
actionName,
params
object
[] args)
{
Type matchedControllerType = FindControllerType(ctrller);
if
(matchedControllerType ==
null
)
{
throw
new
ArgumentException($
"未找到類型爲{ctrller}的Controller類型"
);
}
object
execResult =
null
;
if
(matchedControllerType !=
null
)
{
var
matchedController = (Controller)Activator.CreateInstance(matchedControllerType);
MethodInfo action = FindAction(matchedControllerType, actionName, args);
if
(action ==
null
)
{
throw
new
ArgumentException($
"在{matchedControllerType.FullName}中未找到與方法名:{actionName}及參數個數:{args.Count()}相匹配的方法"
);
}
var
filters = action.GetCustomAttributes<FilterAttribute>(
true
);
List<FilterAttribute> execBeforeFilters =
new
List<FilterAttribute>();
List<FilterAttribute> execAfterFilters =
new
List<FilterAttribute>();
List<FilterAttribute> exceptionFilters =
new
List<FilterAttribute>();
if
(filters !=
null
&& filters.Count() > 0)
{
execBeforeFilters = filters.Where(f => f.FilterType ==
"BEFORE"
).ToList();
execAfterFilters = filters.Where(f => f.FilterType ==
"AFTER"
).ToList();
exceptionFilters = filters.Where(f => f.FilterType ==
"EXCEPTION"
).ToList();
}
try
{
matchedController.OnActionExecuting(action);
if
(execBeforeFilters !=
null
&& execBeforeFilters.Count > 0)
{
execBeforeFilters.ForEach(f => f.Execute(matchedController,
null
));
}
var
mParams = action.GetParameters();
object
[] newArgs =
new
object
[args.Length];
for
(
int
i = 0; i < mParams.Length; i++)
{
newArgs[i] = Convert.ChangeType(args[i], mParams[i].ParameterType);
}
execResult = action.Invoke(matchedController, newArgs);
matchedController.OnActionExecuted(action);
if
(execBeforeFilters !=
null
&& execBeforeFilters.Count > 0)
{
execAfterFilters.ForEach(f => f.Execute(matchedController,
null
));
}
}
catch
(Exception ex)
{
matchedController.OnActionError(action, ex);
if
(exceptionFilters !=
null
&& exceptionFilters.Count > 0)
{
exceptionFilters.ForEach(f => f.Execute(matchedController, ex));
}
}
}
return
execResult;
}
private
Type FindControllerType(
string
ctrller)
{
Type matchedControllerType =
null
;
if
(!matchedControllerTypes.ContainsKey(ctrller))
{
var
assy = Assembly.GetAssembly(
typeof
(Controller));
foreach
(
var
m
in
assy.GetModules(
false
))
{
foreach
(
var
t
in
m.GetTypes())
{
if
(ControllerType.IsAssignableFrom(t) && !t.IsAbstract)
{
if
(t.Name.Equals(ctrller, StringComparison.OrdinalIgnoreCase) || t.Name.Equals($
"{ctrller}Controller"
, StringComparison.OrdinalIgnoreCase))
{
matchedControllerType = t;
matchedControllerTypes[ctrller] = matchedControllerType;
break
;
}
}
}
}
}
else
{
matchedControllerType = matchedControllerTypes[ctrller];
}
return
matchedControllerType;
}
private
MethodInfo FindAction(Type matchedControllerType,
string
actionName,
object
[] args)
{
string
ctrlerWithActionKey = $
"{matchedControllerType.FullName}.{actionName}"
;
MethodInfo action =
null
;
if
(!matchedControllerActions.ContainsKey(ctrlerWithActionKey))
{
if
(args ==
null
) args =
new
object
[0];
foreach
(
var
m
in
matchedControllerType.GetMethods(BindingFlags.Instance | BindingFlags.Public))
{
if
(m.Name.Equals(actionName, StringComparison.OrdinalIgnoreCase) && m.GetParameters().Length == args.Length)
{
action = m;
matchedControllerActions[ctrlerWithActionKey] = action;
break
;
}
}
}
else
{
action = matchedControllerActions[ctrlerWithActionKey];
}
return
action;
}
}
|
使用前,先定義一個繼承自Controller的類,如:TestController,並重寫相應的方法,或在指定的方法上加上所需的過濾器特性,以下代碼所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
public
class
TestController : Controller
{
public
override
void
OnActionExecuting(MethodInfo action)
{
Console.WriteLine($
"{action.Name}執行前,OnActionExecuting---{DateTime.Now.ToString()}"
);
}
public
override
void
OnActionExecuted(MethodInfo action)
{
Console.WriteLine($
"{action.Name}執行後,OnActionExecuted--{DateTime.Now.ToString()}"
);
}
public
override
void
OnActionError(MethodInfo action, Exception ex)
{
Console.WriteLine($
"{action.Name}執行,OnActionError--{DateTime.Now.ToString()}:{ex.Message}"
);
}
[ActionExecutingFilter]
[ActionExecutedFilter]
public
string
HelloWorld(
string
name)
{
return
($
"Hello World!->{name}"
);
}
[ActionExecutingFilter]
[ActionExecutedFilter]
[ActionErrorFilter]
public
string
TestError(
string
name)
{
throw
new
Exception(
"這是測試拋出的錯誤信息!"
);
}
[ActionExecutingFilter]
[ActionExecutedFilter]
public
int
Add(
int
a,
int
b)
{
return
a + b;
}
}
|
最後前端實際調用就很是簡單了,代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
class
MVCProgram
{
static
void
Main(
string
[] args)
{
try
{
var
appContext =
new
AppContext();
object
rs = appContext.Process(
"Test"
,
"HelloWorld"
,
"夢在旅途"
);
Console.WriteLine($
"Process執行的結果1:{rs}"
);
Console.WriteLine(
"="
.PadRight(50,
'='
));
appContext.AddExecRouteTemplate(
"{controller}/{action}/{name}"
);
appContext.AddExecRouteTemplate(
"{action}/{controller}/{name}"
);
object
result1 = appContext.Run(
"HelloWorld/Test/夢在旅途-zuowenjun.cn"
);
Console.WriteLine($
"執行的結果1:{result1}"
);
Console.WriteLine(
"="
.PadRight(50,
'='
));
object
result2 = appContext.Run(
"Test/HelloWorld/夢在旅途-zuowenjun.cn"
);
Console.WriteLine($
"執行的結果2:{result2}"
);
Console.WriteLine(
"="
.PadRight(50,
'='
));
appContext.AddExecRouteTemplate(
"{action}/{controller}/{a}/{b}"
);
object
result3 = appContext.Run(
"Add/Test/500/20"
);
Console.WriteLine($
"執行的結果3:{result3}"
);
object
result4 = appContext.Run(
"Test/TestError/夢在旅途-zuowenjun.cn"
);
Console.WriteLine($
"執行的結果4:{result4}"
);
}
catch
(Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($
"發生錯誤:{ex.Message}"
);
Console.ResetColor();
}
Console.ReadKey();
}
}
|
能夠看到,與ASP.NET MVC有點相似,只是ASP.NET MVC是經過URL訪問,而這裏是經過AppContext.Run 執行路由URL 或Process方法,直接指定Controller、Action、參數來執行。
經過以上調用代碼能夠看出路由配置仍是比較靈活的,固然參數配置除外。若是你們有更好的想法也能夠在下方評論交流,謝謝!
MVC代碼執行效果以下: