適用於app.config與web.config的ConfigUtil讀寫工具類 基於MongoDb官方C#驅動封裝MongoDbCsharpHelper類(CRUD類) 基於ASP.NET WEB

適用於app.config與web.config的ConfigUtil讀寫工具類

 

以前文章:《兩種讀寫配置文件的方案(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  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  in  assy.GetModules( false ))
             {
                 foreach  ( var  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  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代碼執行效果以下:

相關文章
相關標籤/搜索