C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志

时间:2022-02-05 20:10:02

C#实现多级子目录Zip压缩解压实例

参考

https://blog.csdn.net/lki_suidongdong/article/details/20942977

重点:

实现多级子目录的压缩,类似winrar,可以选择是否排除基准目录

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

 1
				public
				void ZipDirectoryTest()
 2
				{
 3
				string path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), DateTime.Now.Ticks.ToString());
 4
				foreach (string sub in
				new
				string[] { "bin", "release", "test", "test\\bin", "" })
 5
				{
 6
				string subPath = System.IO.Path.Combine(path, sub);
 7
				if (!System.IO.Directory.Exists(subPath))
 8
				System.IO.Directory.CreateDirectory(subPath);
 9 System.IO.File.WriteAllText(System.IO.Path.Combine(subPath, "1.cs"), "");
10 System.IO.File.WriteAllText(System.IO.Path.Combine(subPath, "1.txt"), "");
11 System.IO.File.WriteAllText(System.IO.Path.Combine(subPath, "1.html"), "");
12 System.IO.File.WriteAllText(System.IO.Path.Combine(subPath, "1.bin"), "");
13
			
14
				}
15
				Console.WriteLine(path);
16
			
17
				new ZipHelper().ZipDirectory(path, "e:\\temp\\tt.zip",false);
18 ZipHelper.UnZip("e:\\temp\\tt.zip", "e:\\temp\\tt2");
19
				//System.IO.Directory.Delete(path, true);
20
				//Q.Helper.FileHelper.SelectFile(path);
			
21 }

代码

  1
				using System;
  2
			
  3
				using System.Collections.Generic;
  4
			
  5
				using System.Linq;
  6
			
  7
				using System.Text;
  8
			
  9
				using System.IO;
 10
			
 11
				using ICSharpCode.SharpZipLib;
 12
			
 13
				using ICSharpCode.SharpZipLib.Zip;
 14
			
 15
				#if NETSTANDARD2_0
 16
			
 17
				using ICSharpCode.SharpZipLib.Checksum;
 18
			
 19
				#else
			
 20
			
 21
				using ICSharpCode.SharpZipLib.Checksums;
 22
			
 23
				#endif
			
 24
			
 25
			
 26
			
 27
				namespace Q.Helper.Zip
 28
			
 29
				{
 30
			
 31
			
 32
			
 33
				///
				<summary>
			
 34
			
 35
				/// 适用与ZIP压缩
 36
			
 37
				///
				</summary>
			
 38
			
 39
				public
				class ZipHelper
 40
			
 41
				{
 42
			
 43
				public
				int Level
 44
			
 45
				{
 46
			
 47
				get; set;
 48
			
 49
				}
 50
			
 51
				#region 压缩
 52
			
 53
			
 54
			
 55
				///
				<summary>
			
 56
			
 57
				/// 递归压缩文件夹的内部方法(排除相对路径)
 58
			
 59
				///
				</summary>
			
 60
			
 61
				///
				<param name="folderToZip">要压缩的文件夹路径</param>
					
 62
			
 63
				///
				<param name="zipStream">压缩输出流</param>
					
 64
			
 65
				///
				<param name="parentFolderName">此文件夹的上级文件夹</param>
					
 66
			
 67
				///
				<param name="includeFloderName">是否包含目录名</param>
					
 68
			
 69
				///
				<returns></returns>
			
 70
			
 71
				private
				bool ZipDirectory(string folderToZip, ZipOutputStream zipStream, string parentFolderName, bool createBaseFolder = true)
 72
			
 73
				{
 74
			
 75 folderToZip = folderToZip.Replace("\\", "/");
 76
			
 77
				bool result = true;
 78
			
 79
				string[] folders, files;
 80
			
 81 ZipEntry ent = null;
 82
			
 83 FileStream fs = null;
 84
			
 85 Crc32 crc = new Crc32();
 86
			
 87
			
 88
			
 89
				try
			
 90
			
 91
				{
 92
			
 93
				string entPath = Path.Combine(parentFolderName, Path.GetFileName(folderToZip) + "/").Replace("\\", "/");
 94
			
 95
				if (!createBaseFolder)
 96
			
 97 entPath = entPath.Substring(entPath.IndexOf("/") + 1);
 98
			
 99
				if (!string.IsNullOrEmpty(entPath))
100
			
101
				{
102
			
103 ent = new ZipEntry(entPath);
104
			
105
				Console.WriteLine(entPath);
106
			
107
				zipStream.PutNextEntry(ent);
108
			
109
				zipStream.Flush();
110
			
111
				}
112
			
113 files = Directory.GetFiles(folderToZip);
114
			
115
				foreach (string file in files)
116
			
117
				{
118
			
119 fs = File.OpenRead(file);
120
			
121
			
122
			
123
				byte[] buffer = new
				byte[fs.Length];
124
			
125 fs.Read(buffer, 0, buffer.Length);
126
			
127 entPath = Path.Combine(parentFolderName, Path.GetFileName(folderToZip) + "/" + Path.GetFileName(file)).Replace("\\", "/");
128
			
129
				if (!createBaseFolder)
130
			
131 entPath = entPath.Substring(entPath.IndexOf("/") + 1);
132
			
133
				Console.WriteLine(entPath);
134
			
135 ent = new ZipEntry(entPath);
136
			
137 ent.DateTime = DateTime.Now;
138
			
139 ent.Size = fs.Length;
140
			
141
			
142
			
143
				fs.Close();
144
			
145
			
146
			
147
				crc.Reset();
148
			
149
				crc.Update(buffer);
150
			
151
			
152
			
153 ent.Crc = crc.Value;
154
			
155
				zipStream.PutNextEntry(ent);
156
			
157 zipStream.Write(buffer, 0, buffer.Length);
158
			
159
				}
160
			
161
			
162
			
163
				}
164
			
165
				catch (Exception ex)
166
			
167
				{
168
			
169 result = false;
170
			
171
				throw ex;
172
			
173
				}
174
			
175
				finally
			
176
			
177
				{
178
			
179
				if (fs != null)
180
			
181
				{
182
			
183
				fs.Close();
184
			
185
				fs.Dispose();
186
			
187
				}
188
			
189
				if (ent != null)
190
			
191
				{
192
			
193 ent = null;
194
			
195
				}
196
			
197
				GC.Collect();
198
			
199 GC.Collect(1);
200
			
201
				}
202
			
203
			
204
			
205 folders = Directory.GetDirectories(folderToZip);
206
			
207
				//多级递归时需要记住相对目录
			
208
			
209
				foreach (string folder in folders)
210
			
211
				{
212
			
213
				if (!ZipDirectory(folder, zipStream, Path.Combine(parentFolderName, Path.GetFileName(folderToZip)), createBaseFolder))
214
			
215
				return
				false;
216
			
217
				}
218
			
219
				return result;
220
			
221
				}
222
			
223
			
224
			
225
				///
				<summary>
			
226
			
227
				/// 压缩文件夹
228
			
229
				///
				</summary>
			
230
			
231
				///
				<param name="folderToZip">要压缩的文件夹路径</param>
					
232
			
233
				///
				<param name="zipedFile">压缩文件完整路径</param>
					
234
			
235
				///
				<param name="password">密码</param>
					
236
			
237
				///
				<returns>是否压缩成功</returns>
					
238
			
239
				public
				bool ZipDirectory(string folderToZip, string zipedFile, string password, bool includeFloderName = true)
240
			
241
				{
242
			
243
				bool result = false;
244
			
245
				if (!Directory.Exists(folderToZip))
246
			
247
				return result;
248
			
249
			
250
			
251 ZipOutputStream zipStream = new ZipOutputStream(File.Create(zipedFile));
252
			
253
				zipStream.SetLevel(Level);
254
			
255
				if (!string.IsNullOrEmpty(password)) zipStream.Password = password;
256
			
257
			
258
			
259 result = ZipDirectory(folderToZip, zipStream, "", includeFloderName);
260
			
261
			
262
			
263
				zipStream.Finish();
264
			
265
				zipStream.Close();
266
			
267
			
268
			
269
				return result;
270
			
271
				}
272
			
273
			
274
			
275
				///
				<summary>
			
276
			
277
				/// 压缩文件夹
278
			
279
				///
				</summary>
			
280
			
281
				///
				<param name="folderToZip">要压缩的文件夹路径</param>
					
282
			
283
				///
				<param name="zipedFile">压缩文件完整路径</param>
					
284
			
285
				///
				<returns>是否压缩成功</returns>
					
286
			
287
				public
				bool ZipDirectory(string folderToZip, string zipedFile, bool includeFloderName = true)
288
			
289
				{
290
			
291
				bool result = ZipDirectory(folderToZip, zipedFile, "", includeFloderName);
292
			
293
				return result;
294
			
295
				}
296
			
297
			
298
			
299
				///
				<summary>
			
300
			
301
				/// 压缩文件
302
			
303
				///
				</summary>
			
304
			
305
				///
				<param name="fileToZip">要压缩的文件全名</param>
					
306
			
307
				///
				<param name="zipedFile">压缩后的文件名</param>
					
308
			
309
				///
				<param name="password">密码</param>
					
310
			
311
				///
				<returns>压缩结果</returns>
					
312
			
313
				public
				bool ZipFile(string fileToZip, string zipedFile, string password)
314
			
315
				{
316
			
317
				bool result = true;
318
			
319 ZipOutputStream zipStream = null;
320
			
321 FileStream fs = null;
322
			
323 ZipEntry ent = null;
324
			
325
			
326
			
327
				if (!File.Exists(fileToZip))
328
			
329
				return
				false;
330
			
331
			
332
			
333
				try
			
334
			
335
				{
336
			
337 fs = File.OpenRead(fileToZip);
338
			
339
				byte[] buffer = new
				byte[fs.Length];
340
			
341 fs.Read(buffer, 0, buffer.Length);
342
			
343
				fs.Close();
344
			
345
			
346
			
347 fs = File.Create(zipedFile);
348
			
349 zipStream = new ZipOutputStream(fs);
350
			
351
				if (!string.IsNullOrEmpty(password)) zipStream.Password = password;
352
			
353 ent = new ZipEntry(Path.GetFileName(fileToZip));
354
			
355
				zipStream.PutNextEntry(ent);
356
			
357
				zipStream.SetLevel(Level);
358
			
359
			
360
			
361 zipStream.Write(buffer, 0, buffer.Length);
362
			
363
			
364
			
365
				}
366
			
367
				catch
			
368
			
369
				{
370
			
371 result = false;
372
			
373
				}
374
			
375
				finally
			
376
			
377
				{
378
			
379
				if (zipStream != null)
380
			
381
				{
382
			
383
				zipStream.Finish();
384
			
385
				zipStream.Close();
386
			
387
				}
388
			
389
				if (ent != null)
390
			
391
				{
392
			
393 ent = null;
394
			
395
				}
396
			
397
				if (fs != null)
398
			
399
				{
400
			
401
				fs.Close();
402
			
403
				fs.Dispose();
404
			
405
				}
406
			
407
				}
408
			
409
				GC.Collect();
410
			
411 GC.Collect(1);
412
			
413
			
414
			
415
				return result;
416
			
417
				}
418
			
419
			
420
			
421
				///
				<summary>
			
422
			
423
				/// 压缩文件
424
			
425
				///
				</summary>
			
426
			
427
				///
				<param name="fileToZip">要压缩的文件全名</param>
					
428
			
429
				///
				<param name="zipedFile">压缩后的文件名</param>
					
430
			
431
				///
				<returns>压缩结果</returns>
					
432
			
433
				public
				bool ZipFile(string fileToZip, string zipedFile)
434
			
435
				{
436
			
437
				bool result = ZipFile(fileToZip, zipedFile, null);
438
			
439
				return result;
440
			
441
				}
442
			
443
			
444
			
445
				///
				<summary>
			
446
			
447
				/// 压缩文件或文件夹
448
			
449
				///
				</summary>
			
450
			
451
				///
				<param name="fileToZip">要压缩的路径</param>
					
452
			
453
				///
				<param name="zipedFile">压缩后的文件名</param>
					
454
			
455
				///
				<param name="password">密码</param>
					
456
			
457
				///
				<returns>压缩结果</returns>
					
458
			
459
				public
				bool Zip(string fileToZip, string zipedFile, string password)
460
			
461
				{
462
			
463
				bool result = false;
464
			
465
				if (Directory.Exists(fileToZip))
466
			
467 result = ZipDirectory(fileToZip, zipedFile, password);
468
			
469
				else
				if (File.Exists(fileToZip))
470
			
471 result = ZipFile(fileToZip, zipedFile, password);
472
			
473
			
474
			
475
				return result;
476
			
477
				}
478
			
479
			
480
			
481
				///
				<summary>
			
482
			
483
				/// 压缩文件或文件夹
484
			
485
				///
				</summary>
			
486
			
487
				///
				<param name="fileToZip">要压缩的路径</param>
					
488
			
489
				///
				<param name="zipedFile">压缩后的文件名</param>
					
490
			
491
				///
				<returns>压缩结果</returns>
					
492
			
493
				public
				bool Zip(string fileToZip, string zipedFile)
494
			
495
				{
496
			
497
				bool result = Zip(fileToZip, zipedFile, null);
498
			
499
				return result;
500
			
501
			
502
			
503
				}
504
			
505
			
506
			
507
				#endregion
			
508
			
509
			
510
			
511
				#region 解压
512
			
513
			
514
			
515
				///
				<summary>
			
516
			
517
				/// 解压功能(解压压缩文件到指定目录)
518
			
519
				///
				</summary>
			
520
			
521
				///
				<param name="fileToUnZip">待解压的文件</param>
					
522
			
523
				///
				<param name="zipedFolder">指定解压目标目录</param>
					
524
			
525
				///
				<param name="password">密码</param>
					
526
			
527
				///
				<returns>解压结果</returns>
					
528
			
529
				public
				static
				bool UnZip(string fileToUnZip, string zipedFolder, string password)
530
			
531
				{
532
			
533
				bool result = true;
534
			
535
			
536
			
537 ZipInputStream zipStream = null;
538
			
539 ZipEntry ent = null;
540
			
541
				string fileName;
542
			
543
			
544
			
545
				if (!File.Exists(fileToUnZip))
546
			
547
				return
				false;
548
			
549
			
550
			
551
				if (!Directory.Exists(zipedFolder))
552
			
553
				Directory.CreateDirectory(zipedFolder);
554
			
555
			
556
			
557
				try
			
558
			
559
				{
560
			
561 zipStream = new ZipInputStream(File.OpenRead(fileToUnZip));
562
			
563
				if (!string.IsNullOrEmpty(password)) zipStream.Password = password;
564
			
565
				while ((ent = zipStream.GetNextEntry()) != null)
566
			
567
				{
568
			
569
				if (!string.IsNullOrEmpty(ent.Name))
570
			
571
				{
572
			
573 fileName = Path.Combine(zipedFolder, ent.Name);
574
			
575 fileName = fileName.Replace('/', '\\');//change by Mr.HopeGi
			
576
			
577
			
578
			
579
				if (fileName.EndsWith("\\"))
580
			
581
				{
582
			
583
				Directory.CreateDirectory(fileName);
584
			
585
				continue;
586
			
587
				}
588
			
589
				using (FileStream fs = File.Create(fileName))
590
			
591
				{
592
			
593
				int size = 2048;
594
			
595
				byte[] data = new
				byte[size];
596
			
597
				while (true)
598
			
599
				{
600
			
601
			
602
			
603 size = zipStream.Read(data, 0, data.Length);
604
			
605
				if (size > 0)
606
			
607 fs.Write(data, 0, data.Length);
608
			
609
				else
			
610
			
611
				break;
612
			
613
				}
614
			
615
				fs.Flush();
616
			
617
			
618
			
619
				fs.Close();
620
			
621
				new FileInfo(fileName).LastWriteTime = ent.DateTime;
622
			
623
				}
624
			
625
			
626
			
627
				}
628
			
629
				}
630
			
631
				}
632
			
633
				catch
			
634
			
635
				{
636
			
637 result = false;
638
			
639
				}
640
			
641
				finally
			
642
			
643
				{
644
			
645
			
646
			
647
				if (zipStream != null)
648
			
649
				{
650
			
651
				zipStream.Close();
652
			
653
				zipStream.Dispose();
654
			
655
				}
656
			
657
				if (ent != null)
658
			
659
				{
660
			
661 ent = null;
662
			
663
				}
664
			
665
				GC.Collect();
666
			
667 GC.Collect(1);
668
			
669
				}
670
			
671
				return result;
672
			
673
				}
674
			
675
			
676
			
677
				///
				<summary>
			
678
			
679
				/// 解压功能(解压压缩文件到指定目录)
680
			
681
				///
				</summary>
			
682
			
683
				///
				<param name="fileToUnZip">待解压的文件</param>
					
684
			
685
				///
				<param name="zipedFolder">指定解压目标目录</param>
					
686
			
687
				///
				<returns>解压结果</returns>
					
688
			
689
				public
				static
				bool UnZip(string fileToUnZip, string zipedFolder)
690
			
691
				{
692
			
693
				bool result = UnZip(fileToUnZip, zipedFolder, null);
694
			
695
				return result;
696
			
697
				}
698
			
699
			
700
			
701
				#endregion
			
702
			
703
				}
704
			
705 }
冰比冰比冰

NET4.6下的UTC时间转换

int UTCSecond = (int)((DateTimeOffset)DateTime.SpecifyKind(DateTime.Now,DateTimeKind.Local)).ToUnixTimeSeconds();

DateTime time = DateTimeOffset.FromUnixTimeSeconds(UTCSecond).DateTime;

Console.WriteLine("当前utc:{0},秒数:{1},换算时间:{2}", DateTimeOffset.Now, UTCSecond, time.ToString("F"));

UTCSecond = Q.Helper.TimeHelper.ToUTCSecond(DateTime.Now);

time = DateTimeOffset.FromUnixTimeSeconds(UTCSecond).DateTime;

Console.WriteLine("当前utc:{0},秒数:{1},换算时间:{2}", DateTimeOffset.Now, UTCSecond, time.ToString("F"));

Console.Read();

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

QCOMMON请使用NUGET包管理器搜索安装 QCommon

基本原理如下

/// <summary>

/// UTC时间

/// </summary>

static readonly DateTime UTC = TimeZoneInfo.ConvertTimeToUtc(new DateTime(1970, 1, 1), TimeZoneInfo.Utc);

/// <summary>

/// 时间转UTC秒

/// </summary>

/// <param name="dateTime">时间(当前时区)</param>

/// <returns>UTC秒</returns>

public static int ToUTCSecond(this DateTime dateTime)

{

return (int)dateTime.ToUniversalTime().Subtract(UTC).TotalSeconds;

}

/// <summary>

/// UTC秒转当地时间

/// </summary>

/// <param name="second">秒数</param>

/// <returns>当时时间</returns>

public static DateTime ToDateTime(this int second)

{

return UTC.AddSeconds(second).ToLocalTime();

}

[译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了

[译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了

本文首发自:博客园

文章地址: https://www.cnblogs.com/yilezhu/p/9276565.html

园子里关于ASP.NET Core Web API的教程很多,但大多都是使用EF+Mysql或者EF+MSSQL的文章。甚至关于ASP.NET Core Web API中使用Dapper+Mysql组合的文章都很少,更别提Oracel+Dapper组合的文章了,那么今天就带着大家一起翻译一篇国外大牛写的关于ASP.NET Core Web API 开发中使用Oracle+Dapper的组合的文章吧。

注:虽然本文内容是翻译,但是楼主刚在2.1环境是使用成功,中间也没有任何阻碍,只是鉴于本人电脑配置太差无法安装Oracle数据库,所以无法进行演示,再者是表示对原作者的尊重,所以在这里只是对原作内容进行翻译然后加上自己的理解稍作改动。应该能对大家使用Oracle+Dapper组合开发ASP.NET Core Web API 有所帮助。

本文的重点是介绍如何使用Dapper ORM+Oracle数据库的组合来创建ASP.NET Core Web API。首先,在这里,我们不使用SQL ,因为互联网上已有很多文章都是使用SQL Server进行演示的。所以,我想写一篇使用Oracle作为数据库的文章。为了降低数据库访问逻辑的复杂性,我们使用Dapper ORM。那么,让我们赶紧开始实战演练吧。

创建一个ASP.NET Core Web API 项目

如果要创建一个新的ASP.NET Core Web API项目的话,只需要打开Visual Studio 2017版本15.3及以上,然后按照以下步骤操作。

  1. 打开文件菜单,点击新建>>项目
  2. 在新打开的新建项目窗口,首先你需要选择 .NET Framework 4.6及以上版本,然后在左侧面板选择C# ,然后选择 .NET Core
  3. 在右侧面板中选择“.NET Core Web 应用程序” 并且选择项目位置,最后点击“确定”
  4. 在下一个窗口,在众多模板中选择Web API模板

    写如何新建ASP.NET Core Web API 的这些步骤的时候我都嫌累,我想大家应该都知道怎么创建吧!就不上图片了。

    设置Oracle表和存储过程

    首先要为演示创建数据库以及表,我们这里使用Oracle Developer Tools。因为它非常小巧灵活,可以帮助我们顺利的处理Oracle数据库。
    Oracle SQL Developer是一个免费的集成开发环境,可简化传统和云部署中Oracle数据库的开发和管理。SQL Developer提供完整的PL / SQL应用程序端到端开发,运行查询和脚本的工作表,用于管理数据库的DBA控制台,报告界面,完整的数据建模解决方案以及用于迁移第三方数据到Oracle的平台。
    创建一个名为“TEST_DB”的数据库名称,并在其中创建一个表名为“EMPLOYEE”。您可以使用以下语法在“TEST_DB”数据库中创建表。

 CREATE TABLE "TEST_DB"."EMPLOYEE"
  (
   "ID" NUMBER(10,0) GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 100 CACHE 20 NOORDER  NOCYCLE ,
"NAME" VARCHAR2(255 BYTE),
"SALARY" NUMBER(10,0),
"ADDRESS" VARCHAR2(500 BYTE)
  ) SEGMENT CREATION IMMEDIATE
 PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
 STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
 TABLESPACE "TEST_DATA" ;  

我们需要在表中添加一些虚拟数据,以便我们可以直接从PostMan获取数据。所以,我们在这里添加四条记录如下。

Insert into TEST_DB.EMPLOYEE (ID,NAME,SALARY,ADDRESS) values (100,'Mukesh',20000,'India');
Insert into TEST_DB.EMPLOYEE (ID,NAME,SALARY,ADDRESS) values (101,'Rion',28000,'US');
Insert into TEST_DB.EMPLOYEE (ID,NAME,SALARY,ADDRESS) values (102,'Mahesh',10000,'India');
Insert into TEST_DB.EMPLOYEE (ID,NAME,SALARY,ADDRESS) values (103,'Banky',20000,'India'); 

现在我们来创建一个存储过程,用来获取员工记录列表。这里我们使用Cursor返回数据列表作为输出参数。

CREATE OR REPLACE PROCEDURE "TEST_DB"."USP_GETEMPLOYEES" (
    EMPCURSOR OUT SYS_REFCURSOR
)
AS
Begin
Open EMPCURSOR For
SELECT ID, NAME, SALARY,ADDRESS FROM Employee;
End; 

下面我们再创建一个存储过程,它根据员工ID获取员工的个人记录

CREATE OR REPLACE PROCEDURE "TEST_DB"."USP_GETEMPLOYEEDETAILS"
(
  EMP_ID IN INT,
  EMP_DETAIL_CURSOR OUT SYS_REFCURSOR
) AS
BEGIN
    OPEN EMP_DETAIL_CURSOR FOR
    SELECT ID, NAME, SALARY,ADDRESS FROM Employee WHERE ID = EMP_ID;
END;  

安装Dapper ORM

从“工具”菜单的“Nuget包管理器”中打开“包管理器控制台”,然后输入以下命令并按Enter键以安装dapper及其依赖项(如果有)

Install-Package Dapper -Version 1.50.5
当然还有另一个安装方式,具体可以看 [ASP.NET Core WebApi使用Swagger生成api说明文档看这篇就够了][http://www.cnblogs.com/yilezhu/p/9241261.html] 中关于安装Swashbuckle.AspNetCore的步骤
安装完成后,你可以查看下项目大的引用中,是否有“Dapper”的引用,如果有的话表示安装正确

为项目安装Oracle Manage Data Access

我们在Asp.Net Core Web API应用程序中使用Oracle,需要从Core应用程序访问Oracle数据库。要将Oracle数据库与.Net Core应用程序一起使用,我们有Oracle库,它将帮助我们管理数据库访问的逻辑。因此,我们必须安装以下bata的软件包。

Install-Package Oracle.ManagedDataAccess.Core -Version 2.12.0-beta2

添加 Oracle 数据库连接

现在我们已准备好与数据库相关的所有内容,如数据库,表和SP等。要从Web API访问数据库,我们必须像往常一样在“appsettings.json”文件中创建连接字符串。

{
  "Logging": {
    "IncludeScopes": false,
    "Debug": {
      "LogLevel": {
        "Default": "Warning"
      }
    },
    "Console": {
      "LogLevel": {
        "Default": "Warning"
      }
    }
  },
  "ConnectionStrings": {
    "EmployeeConnection": "data source=mukesh:1531;password=**********;user id=mukesh;Incr Pool Size=5;Decr Pool Size=2;"
  }
}  

创建一个仓储

为了保持关注点的分离,我们在这里使用Repository。在Web API项目中创建一个新文件夹作为“仓储库”,并创建一个“IEmployeeRepository”接口和一个它的实现类“EmployeeRepository”,它将实现到IEmployeeRepository。

namespace Core2API.Repositories
{
    public interface IEmployeeRepository
    {
        object GetEmployeeList();  

        object GetEmployeeDetails(int empId);  

    }
}  

以下是实现了IEmployeeRepository的EmployeeRepository类。它需要访问配置中的数据库连接串,因此我们在构造函数中注入IConfiguration。所以,我们已经准备好使用配置对象了。除此之外,我们还有GetConnection()方法,该方法将从appsettings.json获取连接字符串,并将其提供给OracleConnection以创建连接并最终返回连接。我们已经实现了“IEmployeeRepository”,它有两个方法,如GetEmployeeDetails和GetEmployeeList。

using Core2API.Oracle;
using Dapper;
using Microsoft.Extensions.Configuration;
using Oracle.ManagedDataAccess.Client;
using System;
using System.Data;  

namespace Core2API.Repositories
{
    public class EmployeeRepository : IEmployeeRepository
    {
        IConfiguration configuration;
        public EmployeeRepository(IConfiguration _configuration)
        {
            configuration = _configuration;
        }
        public object GetEmployeeDetails(int empId)
        {
            object result = null;
            try
            {
                var dyParam = new OracleDynamicParameters();
                dyParam.Add("EMP_ID", OracleDbType.Int32, ParameterDirection.Input, empId);
                dyParam.Add("EMP_DETAIL_CURSOR", OracleDbType.RefCursor, ParameterDirection.Output);  

                var conn = this.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    conn.Open();
                }  

                if (conn.State == ConnectionState.Open)
                {
                    var query = "USP_GETEMPLOYEEDETAILS";  

                    result = SqlMapper.Query(conn, query, param: dyParam, commandType: CommandType.StoredProcedure);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }  

            return result;
        }  

        public object GetEmployeeList()
        {
            object result = null;
            try
            {
                var dyParam = new OracleDynamicParameters();  

                dyParam.Add("EMPCURSOR", OracleDbType.RefCursor, ParameterDirection.Output);  

                var conn = this.GetConnection();
                if(conn.State == ConnectionState.Closed)
                {
                    conn.Open();
                }  

                if (conn.State == ConnectionState.Open)
                {
                    var query = "USP_GETEMPLOYEES";  

                    result = SqlMapper.Query(conn, query, param: dyParam, commandType: CommandType.StoredProcedure);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }  

            return result;
        }  

        public IDbConnection GetConnection()
        {
            var connectionString = configuration.GetSection("ConnectionStrings").GetSection("EmployeeConnection").Value;
            var conn = new OracleConnection(connectionString);
            return conn;
        }
    }
}  

public IDbConnection GetConnection()
{
     var connectionString = configuration.GetSection("ConnectionStrings").GetSection("EmployeeConnection").Value;
     var conn = new OracleConnection(connectionString);
     return conn;
}  

为了在.Net Core中使用Oracle的数据类型,我们使用的是OracleDyamicParameters类,它将提供管理Oracle参数行为的一系列方法。

using Dapper;
using Oracle.ManagedDataAccess.Client;
using System.Collections.Generic;
using System.Data;  

namespace Core2API.Oracle
{
    public class OracleDynamicParameters : SqlMapper.IDynamicParameters
    {
        private readonly DynamicParameters dynamicParameters = new DynamicParameters();
        private readonly List<OracleParameter> oracleParameters = new List<OracleParameter>();  

        public void Add(string name, OracleDbType oracleDbType, ParameterDirection direction, object value = null, int? size = null)
        {
            OracleParameter oracleParameter;
            if (size.HasValue)
            {
                oracleParameter = new OracleParameter(name, oracleDbType, size.Value, value, direction);
            }
            else
            {
                oracleParameter = new OracleParameter(name, oracleDbType, value, direction);
            }  

            oracleParameters.Add(oracleParameter);
        }  

        public void Add(string name, OracleDbType oracleDbType, ParameterDirection direction)
        {
            var oracleParameter = new OracleParameter(name, oracleDbType, direction);
            oracleParameters.Add(oracleParameter);
        }  

        public void AddParameters(IDbCommand command, SqlMapper.Identity identity)
        {
            ((SqlMapper.IDynamicParameters)dynamicParameters).AddParameters(command, identity);  

            var oracleCommand = command as OracleCommand;  

            if (oracleCommand != null)
            {
                oracleCommand.Parameters.AddRange(oracleParameters.ToArray());
            }
        }
    }
}  

在Startup.cs中配置依赖

如果要在控制器或仓储类中使用依赖项的话,我们必须配置或者说在Startup类的ConfigureServices方法中为我们的接口注册我们的依赖项类。 (翻译的好拗口,楼主四级没过,希望不被喷)

using Core2API.Repositories;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;  

namespace Core2API
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }  

        public IConfiguration Configuration { get; }  

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IEmployeeRepository, EmployeeRepository>();
            services.AddSingleton<IConfiguration>(Configuration);
            services.AddMvc();
        }  

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }  

            app.UseMvc();
        }
    }
}  

添加 EmployeeController 控制器

现在是时候在EmployeeControler中创建API调用了。首先,我们在构造函数中添加了IEmployeeRepository以使用依赖项。其次,我们必须为两个方法创建带有Route属性的API调用。

using Core2API.Repositories;
using Microsoft.AspNetCore.Mvc;  

namespace CoreAPI.Controllers
{
    [Produces("application/json")]
    public class EmployeeController : Controller
    {
        IEmployeeRepository employeeRepository;
        public EmployeeController(IEmployeeRepository _employeeRepository)
        {
            employeeRepository = _employeeRepository;
        }  

        [Route("api/GetEmployeeList")]
        public ActionResult GetEmployeeList()
        {
            var result = employeeRepository.GetEmployeeList();
            if (result == null)
            {
                return NotFound();
            }
            return Ok(result);
        }  

        [Route("api/GetEmployeeDetails/{empId}")]
        public ActionResult GetEmployeeDetails(int empId)
        {
            var result = employeeRepository.GetEmployeeDetails(empId);
            if (result == null)
            {
                return NotFound();
            }
            return Ok(result);
        }
    }
}  

现在我们已准备就绪,就像存储库已准备好,与Oracle数据库的连接已准备就绪,最后,API调用也在控制器内部就绪。因此,是时候在PostMan中运行API来查看结果了。只需按F5即可运行Web API然后打开PostMan进行测试。

要在PostMan中进行测试,首先选择“Get”作为方法,并提供URL以获取员工记录列表,然后单击“发送”按钮,该按钮将向我们的API发出请求并使用我们文章开始时创建的数据库脚本来获取我们在此处添加的员工列表数据。

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

要获取单个员工记录,只需传递以下URL,如图中所示。您可以在此处看到,我们希望查看员工ID 103的记录。发送请求后,您可以看到如下所示的输出。

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

最后

所以,今天,我们已经学会了如何创建ASP.NET Core Web API项目并使用Dapper与Oracle数据库一起使用。

我希望这篇文章能对你有所帮助。请使用评论来进行反馈,这有助于我提高自己的下一篇文章。如果您有任何疑问,请在评论部分发表你的疑问,如果您喜欢这篇文章,请与您的朋友分享。并记得点下推荐哦!

原文地址:https://www.c-sharpcorner.com/article/asp-net-core-web-api-with-oracle-database-and-dapper/
翻译人:依乐祝

总结

今天主要是翻译了一篇国外的使用Dapper以及Oracle的组合来开发asp.net core web api的教程!目的就是填补园子里使用Dapper以及Oracle的组合来开发asp.net core web api的空白!还有就是最近连续出差都没有更新文章了!接下来我会为大家介绍更多asp.net core 微服务相关的技术,希望大家持续关注!如果感觉博主写的还不错的话希望给个推荐!谢谢!

asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程

最近在学习张善友老师的NanoFabric 框架的时了解到Exceptionless : https://exceptionless.com/ !因此学习了一下这个开源框架!下面对Exceptionless的学习做下笔记!

Exceptionless是什么?能做什么呢?

“Exceptionless”这个词的定义是:没有异常。Exceptionless可以为您的ASP.NET、Web API、WebFrm、WPF、控制台和MVC应用程序提供实时错误、特性和日志报告。它将收集的信息组织成简单的可操作的数据,这些数据将帮助你很方便的查看异常信息。还有最重要的是,它是开源的!

Exceptionless的使用方式有哪些?

1.官网创建帐号,并新建应用程序以及项目,然后生成apikey(数据存储在Exceptionless)

2.自己搭建Exceptionless的环境,部署在本地(数据存储在本地)

Exceptionless的运行环境有哪些要求?需要安装哪些软件,进行什么配置呢?

  • .NET 4.6.1 (安装了.net core 或者vs2017的话环境应该都没问题,不需要额外安装)
  • Java JDK 1.8+(如果使用windows系统的话需要配置环境变量,这个使用过java的人应该都知道吧!相信对于你来说应该不是难事).下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
  • IIS Express 8+(win 7以上环境应该都没问题,不需要额外安装)
  • PowerShell 3+(win 7以上环境应该都没问题,不需要额外安装)
  • 这里分win7(管理员身份运行cmd ,然后复制下面这条命令,按回车就行了 powershell Set-ExecutionPolicy Unrestricted) 以及 win10(管理员身份运行powershell,然后执行powershell Set-ExecutionPolicy Unrestricted
  • Elasticsearch 5.6 官方推荐这个版本,(当然你也可以不进行安装,因为后面会教你如何自动安装这个软件)需要在历史版本中找  ,下载地址:https://www.elastic.co/downloads/past-releases

Exceptionless下载以及配置

1.打包下载地址:https://github.com/exceptionless/Exceptionless/releases  如下图所示进行下载就可以了!,别看只有15M有的人下载可能需要半个小时,别问为什么,因为~~~~~

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

2.下载完成之后,右键解压

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

3.看到如下的文件目录结构,有几点需要说明,如果你比较懒,嫌部署到iis比较麻烦,安装Elasticsearch也比较麻烦,那么,你可以双击“Start.bat”这个脚本,它会自动帮你安装Elasticsearch,以及(当然,生产环境,还是建议自己搭建Elasticsearch的好)

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

4.如果出现下图所示,那么你就耐心的等等就行了,运行结束后会自动为您打开Exceptionless的管理页面

,如果不幸,cmd里面出现红色字体,而且一闪就自动退出的话,那就执行下powershell Set-ExecutionPolicy Unrestricted 这个命令,然后再双击“Start.bat”这个脚本运行吧!

  • 这里分win7(管理员身份运行cmd ,然后复制下面这条命令,按回车就行了 powershell Set-ExecutionPolicy Unrestricted) 以及 win10(管理员身份运行powershell,然后执行powershell Set-ExecutionPolicy Unrestricted

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

5.如果全部安装成功后,会自动为你打开几个页面。还是先来看下目录结构吧,如下图所示,默认安装Elasticsearch是5.5.2 同时安装了kibana版本也是5.5.2

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

6.打开的几个页面如下图所示,然后在Exceptionless的页面,点击注册按钮注册一个账号,然后进行登录

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

7.注册成功后,进入如下的界面,在两个文本框输入,组织机构名称以及项目名称,用来对我们的项目的异常进行分类吧

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

8.下面进入项目类型配置界面,在1.select your project type下拉框选择asp.net core

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

9.出现下面的界面,说明配置完成,并且给出使用说明。到此Exceptionless的安装配置已经完成。

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

接下来我们通过一个实例项目进行使用说明吧

1.新建一个 netcore api项目,这一步应该难不倒你吧,我就不上图了。

2.在程序包管理器中,选中你的项目,然后输入“ Install-Package Exceptionless.AspNetCore”安装nuget包吧,当然也可以通过其他方式安装,就不介绍了

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

3.在startup.cs中添加 引用

1
using Exceptionless;

然后在Configure方法中添加Exceptionless管道信息

1
2
3
ExceptionlessClient.Default.Configuration.ApiKey = Configuration.GetSection("Exceptionless:ApiKey").Value;
           ExceptionlessClient.Default.Configuration.ServerUrl = Configuration.GetSection("Exceptionless:ServerUrl").Value;
           app.UseExceptionless();

 然后在appsettings.json中添加apikey以及serverurl的配置

1
2
3
4
"Exceptionless": {
   "ApiKey""OvzcKg8V7bPcWU8yAYBVe6uCEKIAQm3xfEzW5yxp",
   "ServerUrl""http://localhost:50000"
 }

  好了,exceptionless的配置以及完成,接下来就是代码中使用了!

4.代码中使用异常,直接上代码吧!就是在ValuesController中修改下get方法进行下测试,代码很简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// GET api/values
     [HttpGet]
     public ActionResult Get()
     {
         try
         {
             throw new Exception("ExceptionDemo 的异常");
         }
         catch (Exception ex)
         {
             ex.ToExceptionless().Submit();
         }
         return Ok();
     }

  

5.运行起来吧。然后浏览器切换到exceptionless的面板进行查看吧,会自动刷新出现异常信息,如下图 http://localhost:50000/#!/project/5b2663e4e6c0b51dd015bdab/dashboard

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

6.点击进入可以查看详细信息

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

总结:

本文从Exceptionless是什么入手,然后介绍了Exceptionless的安装环境以及要求,接下来通过图文详细的介绍了Exceptionless的安装以及配置。最后通过一个Demo演示了如何在代码中使用Exceptionless,当然只是简单地一些使用!今天的关于asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程的介绍就到这里了!

asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案

之前碰到asp.net core异步进行新增操作并且需要判断某些字段是否重复的问题,进行插入操作的话会导致数据库中插入重复的字段!下面把我的解决方法记录一下,如果对您有所帮助,欢迎拍砖!

场景:EFCore操作MySql数据库的项目,进行高并发插入操作

需求:消息队列,最后进行新增数据的操作,插入前判断某些字段是否重复

问题:采用await db.SaveChangesAsync()进行提交操作前,FirstOrDefault判断数据库中是否有重复数据。测试100条一样的数据进行并发插入,结果数据库中插入成功四条重复数据!

原因分析:有可能是await db.SaveChangesAsync异步进行操作导致的时差问题!

解决方案:

第一种方案: 数据库中对表设置复合主键,即把需要判断不能重复的字段组合起来设置主键(不建议这种方式);

第二种方案:数据库插入操作采用同步的方式进行插入,即:await db.SaveChangesAsync() 改为 db.SaveChanges();

第三种方案:数据库查询操作FirstOrDefault 以及数据库提交插入操作 await db.SaveChangesAsync() 放在一个数据库事务中即用   using (var tran = dBContext.Database.BeginTransaction())包裹下代码即可!

以上就是asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案!希望对您有所帮助!

.NET Core开发日志——Middleware

熟悉ASP.NET架构的开发者一定对于HTTP Modules与HTTP Handlers不陌生。两者的作用主要是对网络请求执行特定的处理工作。而在.NET Core中,它们都被Middleware(中件间)取代了。

之前的Http Modules和HTTP Handlers是如下图般处理请求的:

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

现在变成了这样:

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

一言概括之,Middleware完成了HTTP Modules与HTTP Handlers的原有工作,但又不是简单的化二为一的减法作用。

Middleware减去的其实是与原来ASP.NET中重要的基础——应用程序生命周期事件(application life cycle event)的绑定。

HTTP Modules在初始化时就需要针对HttpApplication的事件作绑定处理,这样当HttpApplication的各项事件被触发时,已绑定的相应处理程序才会按照预期的那样被执行。

public class HelloWorldModule : IHttpModule
{
    public HelloWorldModule()
    {
    }

    public String ModuleName
    {
        get { return "HelloWorldModule"; }
    }

    // In the Init function, register for HttpApplication
    // events by adding your handlers.
    public void Init(HttpApplication application)
    {
        application.BeginRequest +=
            (new EventHandler(this.Application_BeginRequest));
        application.EndRequest +=
            (new EventHandler(this.Application_EndRequest));
    }

    private void Application_BeginRequest(Object source,
         EventArgs e)
    {
    // Create HttpApplication and HttpContext objects to access
    // request and response properties.
        HttpApplication application = (HttpApplication)source;
        HttpContext context = application.Context;
        context.Response.Write("<h1><font color=red>
            HelloWorldModule: Beginning of Request
            </font></h1><hr>");
    }

    private void Application_EndRequest(Object source, EventArgs e)
    {
        HttpApplication application = (HttpApplication)source;
        HttpContext context = application.Context;
        context.Response.Write("<hr><h1><font color=red>
            HelloWorldModule: End of Request</font></h1>");
    }

    public void Dispose()
    {
    }
}

然后你还需要在web.config配置文件注册这个HTTP Module。

<configuration>
    <system.web>
        <httpModules>
           <add name="HelloWorldModule" type="HelloWorldModule"/>
        </httpModules>
    </system.web>
</configuration>

如果是用Middleware的话,事情就变得很简单了。抛弃IHttpModule接口及HttpModule实现类,不用再关心HttpApplication的任何事件,还有烦人的web.config配置。直接在代码中以最简洁的方式完成工作。


public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.Use(async(context, next) =>{
        await context.Response.WriteAsync("Beginning of Request\n");
        await next.Invoke();
        await context.Response.WriteAsync("End of Request\n");
    });
    app.Run(async (context) =>
    {
        await context.Response.WriteAsync("Hello World!\n");
    });
}

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

相似的,对于HTTP Handlers,虽然不用取消对HttpApplication事件的依赖,但以两者的代码实现方式作比较,Middleware亳无疑问胜出一筹。

public class HelloWorldHandler : IHttpHandler
{
    public HelloWorldHandler()
    {
    }
    public void ProcessRequest(HttpContext context)
    {
        HttpRequest Request = context.Request;
        HttpResponse Response = context.Response;
        // This handler is called whenever a file ending
        // in .sample is requested. A file with that extension
        // does not need to exist.
        Response.Write("<html>");
        Response.Write("<body>");
        Response.Write("<h1>Hello from a synchronous custom HTTP handler.</h1>");
        Response.Write("</body>");
        Response.Write("</html>");
    }
    public bool IsReusable
    {
        // To enable pooling, return true here.
        // This keeps the handler in memory.
        get { return false; }
    }
}

仍需要在web.config文件中注册HTTP handler。

<configuration>
    <system.web>
        <httpHandlers>
            <add verb="*" path="*.sample"
                  type="HelloWorldHandler"/>
        </httpHandlers>
    </system.web>
</configuration>

换作Middleware的写法:

private static void HandleSample(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Hello Sample");
    });
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.MapWhen(context => context.Request.Path.Value.EndsWith("sample"), HandleSample);
}

C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志

总结下使用Middleware的优点:

  • 没有对HttpApplication的依赖
  • 没有对IHttpModule与IHttpHandler接口的依赖
  • 无需在web.config文件中添加各种配置
  • 代码简洁

最后需要补充Middleware与HTTP Modules的一点差异。各Middleware中处理请求与响应的顺序是刚好相反的,越早处理请求的Middleware越晚处理响应。而HTTP Modules中处理请求与响应的顺序则保持一致,因为每个HTTP Module请求与响应事件的绑定都是在同一阶段完成的。