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

参考

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

重点:

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

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

代码

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

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”数据库中创建表。

  1. CREATE TABLE "TEST_DB"."EMPLOYEE"
  2. (
  3. "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 ,
  4. "NAME" VARCHAR2(255 BYTE),
  5. "SALARY" NUMBER(10,0),
  6. "ADDRESS" VARCHAR2(500 BYTE)
  7. ) SEGMENT CREATION IMMEDIATE
  8. PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
  9. NOCOMPRESS LOGGING
  10. STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  11. PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  12. BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  13. TABLESPACE "TEST_DATA" ;

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

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

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

  1. CREATE OR REPLACE PROCEDURE "TEST_DB"."USP_GETEMPLOYEES" (
  2. EMPCURSOR OUT SYS_REFCURSOR
  3. )
  4. AS
  5. Begin
  6. Open EMPCURSOR For
  7. SELECT ID, NAME, SALARY,ADDRESS FROM Employee;
  8. End;

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

  1. CREATE OR REPLACE PROCEDURE "TEST_DB"."USP_GETEMPLOYEEDETAILS"
  2. (
  3. EMP_ID IN INT,
  4. EMP_DETAIL_CURSOR OUT SYS_REFCURSOR
  5. ) AS
  6. BEGIN
  7. OPEN EMP_DETAIL_CURSOR FOR
  8. SELECT ID, NAME, SALARY,ADDRESS FROM Employee WHERE ID = EMP_ID;
  9. 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”文件中创建连接字符串。

  1. {
  2. "Logging": {
  3. "IncludeScopes": false,
  4. "Debug": {
  5. "LogLevel": {
  6. "Default": "Warning"
  7. }
  8. },
  9. "Console": {
  10. "LogLevel": {
  11. "Default": "Warning"
  12. }
  13. }
  14. },
  15. "ConnectionStrings": {
  16. "EmployeeConnection": "data source=mukesh:1531;password=**********;user id=mukesh;Incr Pool Size=5;Decr Pool Size=2;"
  17. }
  18. }

创建一个仓储

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

  1. namespace Core2API.Repositories
  2. {
  3. public interface IEmployeeRepository
  4. {
  5. object GetEmployeeList();
  6. object GetEmployeeDetails(int empId);
  7. }
  8. }

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

  1. using Core2API.Oracle;
  2. using Dapper;
  3. using Microsoft.Extensions.Configuration;
  4. using Oracle.ManagedDataAccess.Client;
  5. using System;
  6. using System.Data;
  7. namespace Core2API.Repositories
  8. {
  9. public class EmployeeRepository : IEmployeeRepository
  10. {
  11. IConfiguration configuration;
  12. public EmployeeRepository(IConfiguration _configuration)
  13. {
  14. configuration = _configuration;
  15. }
  16. public object GetEmployeeDetails(int empId)
  17. {
  18. object result = null;
  19. try
  20. {
  21. var dyParam = new OracleDynamicParameters();
  22. dyParam.Add("EMP_ID", OracleDbType.Int32, ParameterDirection.Input, empId);
  23. dyParam.Add("EMP_DETAIL_CURSOR", OracleDbType.RefCursor, ParameterDirection.Output);
  24. var conn = this.GetConnection();
  25. if (conn.State == ConnectionState.Closed)
  26. {
  27. conn.Open();
  28. }
  29. if (conn.State == ConnectionState.Open)
  30. {
  31. var query = "USP_GETEMPLOYEEDETAILS";
  32. result = SqlMapper.Query(conn, query, param: dyParam, commandType: CommandType.StoredProcedure);
  33. }
  34. }
  35. catch (Exception ex)
  36. {
  37. throw ex;
  38. }
  39. return result;
  40. }
  41. public object GetEmployeeList()
  42. {
  43. object result = null;
  44. try
  45. {
  46. var dyParam = new OracleDynamicParameters();
  47. dyParam.Add("EMPCURSOR", OracleDbType.RefCursor, ParameterDirection.Output);
  48. var conn = this.GetConnection();
  49. if(conn.State == ConnectionState.Closed)
  50. {
  51. conn.Open();
  52. }
  53. if (conn.State == ConnectionState.Open)
  54. {
  55. var query = "USP_GETEMPLOYEES";
  56. result = SqlMapper.Query(conn, query, param: dyParam, commandType: CommandType.StoredProcedure);
  57. }
  58. }
  59. catch (Exception ex)
  60. {
  61. throw ex;
  62. }
  63. return result;
  64. }
  65. public IDbConnection GetConnection()
  66. {
  67. var connectionString = configuration.GetSection("ConnectionStrings").GetSection("EmployeeConnection").Value;
  68. var conn = new OracleConnection(connectionString);
  69. return conn;
  70. }
  71. }
  72. }
  73. public IDbConnection GetConnection()
  74. {
  75. var connectionString = configuration.GetSection("ConnectionStrings").GetSection("EmployeeConnection").Value;
  76. var conn = new OracleConnection(connectionString);
  77. return conn;
  78. }

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

  1. using Dapper;
  2. using Oracle.ManagedDataAccess.Client;
  3. using System.Collections.Generic;
  4. using System.Data;
  5. namespace Core2API.Oracle
  6. {
  7. public class OracleDynamicParameters : SqlMapper.IDynamicParameters
  8. {
  9. private readonly DynamicParameters dynamicParameters = new DynamicParameters();
  10. private readonly List<OracleParameter> oracleParameters = new List<OracleParameter>();
  11. public void Add(string name, OracleDbType oracleDbType, ParameterDirection direction, object value = null, int? size = null)
  12. {
  13. OracleParameter oracleParameter;
  14. if (size.HasValue)
  15. {
  16. oracleParameter = new OracleParameter(name, oracleDbType, size.Value, value, direction);
  17. }
  18. else
  19. {
  20. oracleParameter = new OracleParameter(name, oracleDbType, value, direction);
  21. }
  22. oracleParameters.Add(oracleParameter);
  23. }
  24. public void Add(string name, OracleDbType oracleDbType, ParameterDirection direction)
  25. {
  26. var oracleParameter = new OracleParameter(name, oracleDbType, direction);
  27. oracleParameters.Add(oracleParameter);
  28. }
  29. public void AddParameters(IDbCommand command, SqlMapper.Identity identity)
  30. {
  31. ((SqlMapper.IDynamicParameters)dynamicParameters).AddParameters(command, identity);
  32. var oracleCommand = command as OracleCommand;
  33. if (oracleCommand != null)
  34. {
  35. oracleCommand.Parameters.AddRange(oracleParameters.ToArray());
  36. }
  37. }
  38. }
  39. }

在Startup.cs中配置依赖

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

  1. using Core2API.Repositories;
  2. using Microsoft.AspNetCore.Builder;
  3. using Microsoft.AspNetCore.Hosting;
  4. using Microsoft.Extensions.Configuration;
  5. using Microsoft.Extensions.DependencyInjection;
  6. namespace Core2API
  7. {
  8. public class Startup
  9. {
  10. public Startup(IConfiguration configuration)
  11. {
  12. Configuration = configuration;
  13. }
  14. public IConfiguration Configuration { get; }
  15. // This method gets called by the runtime. Use this method to add services to the container.
  16. public void ConfigureServices(IServiceCollection services)
  17. {
  18. services.AddTransient<IEmployeeRepository, EmployeeRepository>();
  19. services.AddSingleton<IConfiguration>(Configuration);
  20. services.AddMvc();
  21. }
  22. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  23. public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  24. {
  25. if (env.IsDevelopment())
  26. {
  27. app.UseDeveloperExceptionPage();
  28. }
  29. app.UseMvc();
  30. }
  31. }
  32. }

添加 EmployeeController 控制器

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

  1. using Core2API.Repositories;
  2. using Microsoft.AspNetCore.Mvc;
  3. namespace CoreAPI.Controllers
  4. {
  5. [Produces("application/json")]
  6. public class EmployeeController : Controller
  7. {
  8. IEmployeeRepository employeeRepository;
  9. public EmployeeController(IEmployeeRepository _employeeRepository)
  10. {
  11. employeeRepository = _employeeRepository;
  12. }
  13. [Route("api/GetEmployeeList")]
  14. public ActionResult GetEmployeeList()
  15. {
  16. var result = employeeRepository.GetEmployeeList();
  17. if (result == null)
  18. {
  19. return NotFound();
  20. }
  21. return Ok(result);
  22. }
  23. [Route("api/GetEmployeeDetails/{empId}")]
  24. public ActionResult GetEmployeeDetails(int empId)
  25. {
  26. var result = employeeRepository.GetEmployeeDetails(empId);
  27. if (result == null)
  28. {
  29. return NotFound();
  30. }
  31. return Ok(result);
  32. }
  33. }
  34. }

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

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

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

最后

所以,今天,我们已经学会了如何创建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有的人下载可能需要半个小时,别问为什么,因为~~~~~

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

总结:

本文从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是如下图般处理请求的:

现在变成了这样:

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

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

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

  1. public class HelloWorldModule : IHttpModule
  2. {
  3. public HelloWorldModule()
  4. {
  5. }
  6. public String ModuleName
  7. {
  8. get { return "HelloWorldModule"; }
  9. }
  10. // In the Init function, register for HttpApplication
  11. // events by adding your handlers.
  12. public void Init(HttpApplication application)
  13. {
  14. application.BeginRequest +=
  15. (new EventHandler(this.Application_BeginRequest));
  16. application.EndRequest +=
  17. (new EventHandler(this.Application_EndRequest));
  18. }
  19. private void Application_BeginRequest(Object source,
  20. EventArgs e)
  21. {
  22. // Create HttpApplication and HttpContext objects to access
  23. // request and response properties.
  24. HttpApplication application = (HttpApplication)source;
  25. HttpContext context = application.Context;
  26. context.Response.Write("<h1><font color=red>
  27. HelloWorldModule: Beginning of Request
  28. </font></h1><hr>");
  29. }
  30. private void Application_EndRequest(Object source, EventArgs e)
  31. {
  32. HttpApplication application = (HttpApplication)source;
  33. HttpContext context = application.Context;
  34. context.Response.Write("<hr><h1><font color=red>
  35. HelloWorldModule: End of Request</font></h1>");
  36. }
  37. public void Dispose()
  38. {
  39. }
  40. }

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

  1. <configuration>
  2. <system.web>
  3. <httpModules>
  4. <add name="HelloWorldModule" type="HelloWorldModule"/>
  5. </httpModules>
  6. </system.web>
  7. </configuration>

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

  1. public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  2. {
  3. app.Use(async(context, next) =>{
  4. await context.Response.WriteAsync("Beginning of Request\n");
  5. await next.Invoke();
  6. await context.Response.WriteAsync("End of Request\n");
  7. });
  8. app.Run(async (context) =>
  9. {
  10. await context.Response.WriteAsync("Hello World!\n");
  11. });
  12. }

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

  1. public class HelloWorldHandler : IHttpHandler
  2. {
  3. public HelloWorldHandler()
  4. {
  5. }
  6. public void ProcessRequest(HttpContext context)
  7. {
  8. HttpRequest Request = context.Request;
  9. HttpResponse Response = context.Response;
  10. // This handler is called whenever a file ending
  11. // in .sample is requested. A file with that extension
  12. // does not need to exist.
  13. Response.Write("<html>");
  14. Response.Write("<body>");
  15. Response.Write("<h1>Hello from a synchronous custom HTTP handler.</h1>");
  16. Response.Write("</body>");
  17. Response.Write("</html>");
  18. }
  19. public bool IsReusable
  20. {
  21. // To enable pooling, return true here.
  22. // This keeps the handler in memory.
  23. get { return false; }
  24. }
  25. }

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

  1. <configuration>
  2. <system.web>
  3. <httpHandlers>
  4. <add verb="*" path="*.sample"
  5. type="HelloWorldHandler"/>
  6. </httpHandlers>
  7. </system.web>
  8. </configuration>

换作Middleware的写法:

  1. private static void HandleSample(IApplicationBuilder app)
  2. {
  3. app.Run(async context =>
  4. {
  5. await context.Response.WriteAsync("Hello Sample");
  6. });
  7. }
  8. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  9. public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  10. {
  11. app.MapWhen(context => context.Request.Path.Value.EndsWith("sample"), HandleSample);
  12. }

总结下使用Middleware的优点:

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

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

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

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

    [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 本文首发自:博客园 文章地址: https://www.cnblogs.com/yilezhu/p/ ...

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

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

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

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

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

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

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

          参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩,类似winrar,可以选择 ...

  6. C#实现Zip压缩解压实例【转】

    本文只列举一个压缩帮助类,使用的是有要添加一个dll引用ICSharpCode.SharpZipLib.dll[下载地址]. 另外说明一下的是,这个类压缩格式是ZIP的,所以文件的后缀写成 .zip. ...

  7. C#实现Zip压缩解压实例

    原文地址:https://www.cnblogs.com/GoCircle/p/6544678.html 本文只列举一个压缩帮助类,使用的是有要添加一个dll引用ICSharpCode.SharpZi ...

  8. Net Core免费开源分布式异常日志收集框架Exceptionless

    asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 https://www.cnblogs.com/yilezhu/p/9193723.htm ...

  9. linux查看文件夹大小,备份文件夹zip压缩解压

    linux查看文件夹大小,备份文件夹zip压缩解压 du -sh : 查看当前目录总共占的容量.而不单独列出各子项占用的容量 du -lh --max-depth=1 : 查看当前目录下一级子文件和子 ...

随机推荐

  1. Nodejs项目网页图标的处理

    今天,我要说的是Nodejs中,关于网页图标的处理. 在讲解怎么处理之前,我们的了解一下什么是网页图标.网页图标就是我们网页打开之后,标签页的图标,比如下面这个 前面的小人就是我们博客园的网页图标. ...

  2. leetcode 15 3sum & leetcode 18 4sum

    3sum: 1 class Solution { public: vector<vector<int>> threeSum(vector<int>& num ...

  3. nodejs安装教程

    http://www.runoob.com/nodejs/nodejs-install-setup.html nodejs官方下载,之后配置环境path,npm随着nodejs安装,自动安装 查看no ...

  4. 后缀数组基本问题QAQ

    以下题目均来自罗穗骞的论文... No.1最长公共前缀 最长公共前缀: 题目: 给定一个字符串,询问某两个后缀的最长公共前缀. 分析: 某两个后缀的最长公共前缀就是区间height最小值,转化为RMQ ...

  5. jQuery重要插件!

    原文发布时间为:2009-08-05 -- 来源于本人的百度文章 [由搬家工具导入] 强烈推荐:240多个jQuery插件 概述 jQuery 是继 prototype 之后又一个优秀的 Javasc ...

  6. div切换 div轮换显示

    原文发布时间为:2009-05-10 -- 来源于本人的百度文章 [由搬家工具导入] <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Tran ...

  7. 高级参数绑定(数组和List绑定)

    1.绑定数组: (1) 需求 在商品列表页面选中多个商品,然后删除. (2). 需求分析 功能要求商品列表页面中的每个商品前有一个checkbok,选中多个商品后点击删除按钮把商品id传递给Contr ...

  8. Document类

    一.类结构 org.jsoup.nodes Class Document java.lang.Object org.jsoup.nodes.Node org.jsoup.nodes.Element o ...

  9. AndroidStudio不重新运行,Debug调试已有进程

    们在使用AndroidStudio进行编写Android应用程序的时候,经常需要对抛出的问题进行断点调试跟踪,如果不知道怎样直接调试已经运行的进程.那么调试的效率会非常低下!下面我就来教大家如何快速调 ...

  10. Android 项目提交到svn需要忽略的文件和文件夹